From 0cc138dc2ed8e810ca692d73e6e8fa4b73e677c2 Mon Sep 17 00:00:00 2001 From: Steven Edwards Date: Mon, 25 Jul 2005 11:08:30 +0000 Subject: [PATCH] Move and forward lz32 functions to kernel32. --- dlls/kernel/Makefile.in | 1 + dlls/kernel/kernel32.spec | 12 + dlls/kernel/lzexpand.c | 598 +++++++++++++++++++++++++++++++++ dlls/lzexpand/lz32.spec | 24 +- dlls/lzexpand/lzexpand_main.c | 599 +--------------------------------- 5 files changed, 624 insertions(+), 610 deletions(-) create mode 100644 dlls/kernel/lzexpand.c diff --git a/dlls/kernel/Makefile.in b/dlls/kernel/Makefile.in index f35c11dd5a2..36d8a7ed9fc 100644 --- a/dlls/kernel/Makefile.in +++ b/dlls/kernel/Makefile.in @@ -41,6 +41,7 @@ C_SRCS = \ lcformat.c \ local16.c \ locale.c \ + lzexpand.c \ module.c \ ne_module.c \ ne_segment.c \ diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index 5c88219b4c4..50862c02af9 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -214,6 +214,7 @@ @ stdcall CopyFileExA (str str ptr ptr ptr long) @ stdcall CopyFileExW (wstr wstr ptr ptr ptr long) @ stdcall CopyFileW(wstr wstr long) +@ stdcall CopyLZFile(long long) @ stdcall CreateActCtxA(ptr) @ stdcall CreateActCtxW(ptr) @ stdcall CreateConsoleScreenBuffer(long long ptr long ptr) @@ -484,6 +485,8 @@ @ stub GetErrorMode @ stdcall GetExitCodeProcess(long ptr) @ stdcall GetExitCodeThread(long ptr) +@ stdcall GetExpandedNameA(str ptr) +@ stdcall GetExpandedNameW(wstr ptr) @ stdcall GetFileAttributesA(str) @ stdcall GetFileAttributesExA(str long ptr) @ stdcall GetFileAttributesExW(wstr long ptr) @@ -714,6 +717,15 @@ @ stdcall LockFile(long long long long long) @ stdcall LockFileEx(long long long long long ptr) @ stdcall LockResource(long) +@ stdcall LZClose(long) +@ stdcall LZCopy(long long) +@ stdcall LZDone() +@ stdcall LZInit(long) +@ stdcall LZOpenFileA(str ptr long) +@ stdcall LZOpenFileW(wstr ptr long) +@ stdcall LZRead(long ptr long) +@ stdcall LZSeek(long long long) +@ stdcall LZStart() @ stdcall MakeCriticalSectionGlobal(ptr) @ stdcall -i386 MapHInstLS() @ stdcall -i386 MapHInstLS_PN() diff --git a/dlls/kernel/lzexpand.c b/dlls/kernel/lzexpand.c new file mode 100644 index 00000000000..8d560d6f41b --- /dev/null +++ b/dlls/kernel/lzexpand.c @@ -0,0 +1,598 @@ +/* + * LZ Decompression functions + * + * Copyright 1996 Marcus Meissner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * NOTES + * + * The LZ (Lempel Ziv) decompression was used in win16 installation programs. + * It is a simple tabledriven decompression engine, the algorithm is not + * documented as far as I know. WINE does not contain a compressor for + * this format. + * + * The implementation is complete and there have been no reports of failures + * for some time. + * + * TODO: + * + * o Check whether the return values are correct + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "windef.h" +#include "winbase.h" +#include "lzexpand.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(file); + +/* The readahead length of the decompressor. Reading single bytes + * using _lread() would be SLOW. + */ +#define GETLEN 2048 + +/* Format of first 14 byte of LZ compressed file */ +struct lzfileheader { + BYTE magic[8]; + BYTE compressiontype; + CHAR lastchar; + DWORD reallength; +}; +static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33}; + +struct lzstate { + HFILE realfd; /* the real filedescriptor */ + CHAR lastchar; /* the last char of the filename */ + + DWORD reallength; /* the decompressed length of the file */ + DWORD realcurrent; /* the position the decompressor currently is */ + DWORD realwanted; /* the position the user wants to read from */ + + BYTE table[0x1000]; /* the rotating LZ table */ + UINT curtabent; /* CURrent TABle ENTry */ + + BYTE stringlen; /* length and position of current string */ + DWORD stringpos; /* from stringtable */ + + + WORD bytetype; /* bitmask within blocks */ + + BYTE *get; /* GETLEN bytes */ + DWORD getcur; /* current read */ + DWORD getlen; /* length last got */ +}; + +#define MAX_LZSTATES 16 +static struct lzstate *lzstates[MAX_LZSTATES]; + +#define IS_LZ_HANDLE(h) (((h) >= 0x400) && ((h) < 0x400+MAX_LZSTATES)) +#define GET_LZ_STATE(h) (IS_LZ_HANDLE(h) ? lzstates[(h)-0x400] : NULL) + +/* reads one compressed byte, including buffering */ +#define GET(lzs,b) _lzget(lzs,&b) +#define GET_FLUSH(lzs) lzs->getcur=lzs->getlen; + +static int +_lzget(struct lzstate *lzs,BYTE *b) { + if (lzs->getcurgetlen) { + *b = lzs->get[lzs->getcur++]; + return 1; + } else { + int ret = _lread(lzs->realfd,lzs->get,GETLEN); + if (ret==HFILE_ERROR) + return HFILE_ERROR; + if (ret==0) + return 0; + lzs->getlen = ret; + lzs->getcur = 1; + *b = *(lzs->get); + return 1; + } +} +/* internal function, reads lzheader + * returns BADINHANDLE for non filedescriptors + * return 0 for file not compressed using LZ + * return UNKNOWNALG for unknown algorithm + * returns lzfileheader in *head + */ +static INT read_header(HFILE fd,struct lzfileheader *head) +{ + BYTE buf[14]; + + if (_llseek(fd,0,SEEK_SET)==-1) + return LZERROR_BADINHANDLE; + + /* We can't directly read the lzfileheader struct due to + * structure element alignment + */ + if (_lread(fd,buf,14)<14) + return 0; + memcpy(head->magic,buf,8); + memcpy(&(head->compressiontype),buf+8,1); + memcpy(&(head->lastchar),buf+9,1); + + /* FIXME: consider endianess on non-intel architectures */ + memcpy(&(head->reallength),buf+10,4); + + if (memcmp(head->magic,LZMagic,8)) + return 0; + if (head->compressiontype!='A') + return LZERROR_UNKNOWNALG; + return 1; +} + + +/*********************************************************************** + * LZStart (LZ32.@) + */ +INT WINAPI LZStart(void) +{ + TRACE("(void)\n"); + return 1; +} + + +/*********************************************************************** + * LZInit (LZ32.@) + * + * initializes internal decompression buffers, returns lzfiledescriptor. + * (return value the same as hfSrc, if hfSrc is not compressed) + * on failure, returns error code <0 + * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process) + * + * since _llseek uses the same types as libc.lseek, we just use the macros of + * libc + */ +HFILE WINAPI LZInit( HFILE hfSrc ) +{ + + struct lzfileheader head; + struct lzstate *lzs; + DWORD ret; + int i; + + TRACE("(%d)\n",hfSrc); + ret=read_header(hfSrc,&head); + if (ret<=0) { + _llseek(hfSrc,0,SEEK_SET); + return ret?ret:hfSrc; + } + for (i = 0; i < MAX_LZSTATES; i++) if (!lzstates[i]) break; + if (i == MAX_LZSTATES) return LZERROR_GLOBALLOC; + lzstates[i] = lzs = HeapAlloc( GetProcessHeap(), 0, sizeof(struct lzstate) ); + if(lzs == NULL) return LZERROR_GLOBALLOC; + + memset(lzs,'\0',sizeof(*lzs)); + lzs->realfd = hfSrc; + lzs->lastchar = head.lastchar; + lzs->reallength = head.reallength; + + lzs->get = HeapAlloc( GetProcessHeap(), 0, GETLEN ); + lzs->getlen = 0; + lzs->getcur = 0; + + if(lzs->get == NULL) { + HeapFree(GetProcessHeap(), 0, lzs); + lzstates[i] = NULL; + return LZERROR_GLOBALLOC; + } + + /* Yes, preinitialize with spaces */ + memset(lzs->table,' ',0x1000); + /* Yes, start 16 byte from the END of the table */ + lzs->curtabent = 0xff0; + return 0x400 + i; +} + + +/*********************************************************************** + * LZDone (LZEXPAND.9) + * LZDone (LZ32.@) + */ +void WINAPI LZDone(void) +{ + TRACE("(void)\n"); +} + + +/*********************************************************************** + * GetExpandedNameA (LZ32.@) + * + * gets the full filename of the compressed file 'in' by opening it + * and reading the header + * + * "file." is being translated to "file" + * "file.bl_" (with lastchar 'a') is being translated to "file.bla" + * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA" + */ + +INT WINAPI GetExpandedNameA( LPSTR in, LPSTR out ) +{ + struct lzfileheader head; + HFILE fd; + OFSTRUCT ofs; + INT fnislowercased,ret,len; + LPSTR s,t; + + TRACE("(%s)\n",in); + fd=OpenFile(in,&ofs,OF_READ); + if (fd==HFILE_ERROR) + return (INT)(INT16)LZERROR_BADINHANDLE; + strcpy(out,in); + ret=read_header(fd,&head); + if (ret<=0) { + /* not a LZ compressed file, so the expanded name is the same + * as the input name */ + _lclose(fd); + return 1; + } + + + /* look for directory prefix and skip it. */ + s=out; + while (NULL!=(t=strpbrk(s,"/\\:"))) + s=t+1; + + /* now mangle the basename */ + if (!*s) { + /* FIXME: hmm. shouldn't happen? */ + WARN("Specified a directory or what? (%s)\n",in); + _lclose(fd); + return 1; + } + /* see if we should use lowercase or uppercase on the last char */ + fnislowercased=1; + t=s+strlen(s)-1; + while (t>=out) { + if (!isalpha(*t)) { + t--; + continue; + } + fnislowercased=islower(*t); + break; + } + if (isalpha(head.lastchar)) { + if (fnislowercased) + head.lastchar=tolower(head.lastchar); + else + head.lastchar=toupper(head.lastchar); + } + + /* now look where to replace the last character */ + if (NULL!=(t=strchr(s,'.'))) { + if (t[1]=='\0') { + t[0]='\0'; + } else { + len=strlen(t)-1; + if (t[len]=='_') + t[len]=head.lastchar; + } + } /* else no modification necessary */ + _lclose(fd); + return 1; +} + + +/*********************************************************************** + * GetExpandedNameW (LZ32.@) + */ +INT WINAPI GetExpandedNameW( LPWSTR in, LPWSTR out ) +{ + INT ret; + DWORD len = WideCharToMultiByte( CP_ACP, 0, in, -1, NULL, 0, NULL, NULL ); + char *xin = HeapAlloc( GetProcessHeap(), 0, len ); + char *xout = HeapAlloc( GetProcessHeap(), 0, len+3 ); + WideCharToMultiByte( CP_ACP, 0, in, -1, xin, len, NULL, NULL ); + if ((ret = GetExpandedNameA( xin, xout )) > 0) + MultiByteToWideChar( CP_ACP, 0, xout, -1, out, strlenW(in)+4 ); + HeapFree( GetProcessHeap(), 0, xin ); + HeapFree( GetProcessHeap(), 0, xout ); + return ret; +} + + +/*********************************************************************** + * LZRead (LZ32.@) + */ +INT WINAPI LZRead( HFILE fd, LPSTR vbuf, INT toread ) +{ + int howmuch; + BYTE b,*buf; + struct lzstate *lzs; + + buf=(LPBYTE)vbuf; + TRACE("(%d,%p,%d)\n",fd,buf,toread); + howmuch=toread; + if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread); + +/* The decompressor itself is in a define, cause we need it twice + * in this function. (the decompressed byte will be in b) + */ +#define DECOMPRESS_ONE_BYTE \ + if (lzs->stringlen) { \ + b = lzs->table[lzs->stringpos]; \ + lzs->stringpos = (lzs->stringpos+1)&0xFFF; \ + lzs->stringlen--; \ + } else { \ + if (!(lzs->bytetype&0x100)) { \ + if (1!=GET(lzs,b)) \ + return toread-howmuch; \ + lzs->bytetype = b|0xFF00; \ + } \ + if (lzs->bytetype & 1) { \ + if (1!=GET(lzs,b)) \ + return toread-howmuch; \ + } else { \ + BYTE b1,b2; \ + \ + if (1!=GET(lzs,b1)) \ + return toread-howmuch; \ + if (1!=GET(lzs,b2)) \ + return toread-howmuch; \ + /* Format: \ + * b1 b2 \ + * AB CD \ + * where CAB is the stringoffset in the table\ + * and D+3 is the len of the string \ + */ \ + lzs->stringpos = b1|((b2&0xf0)<<4); \ + lzs->stringlen = (b2&0xf)+2; \ + /* 3, but we use a byte already below ... */\ + b = lzs->table[lzs->stringpos];\ + lzs->stringpos = (lzs->stringpos+1)&0xFFF;\ + } \ + lzs->bytetype>>=1; \ + } \ + /* store b in table */ \ + lzs->table[lzs->curtabent++]= b; \ + lzs->curtabent &= 0xFFF; \ + lzs->realcurrent++; + + /* if someone has seeked, we have to bring the decompressor + * to that position + */ + if (lzs->realcurrent!=lzs->realwanted) { + /* if the wanted position is before the current position + * I see no easy way to unroll ... We have to restart at + * the beginning. *sigh* + */ + if (lzs->realcurrent>lzs->realwanted) { + /* flush decompressor state */ + _llseek(lzs->realfd,14,SEEK_SET); + GET_FLUSH(lzs); + lzs->realcurrent= 0; + lzs->bytetype = 0; + lzs->stringlen = 0; + memset(lzs->table,' ',0x1000); + lzs->curtabent = 0xFF0; + } + while (lzs->realcurrentrealwanted) { + DECOMPRESS_ONE_BYTE; + } + } + + while (howmuch) { + DECOMPRESS_ONE_BYTE; + lzs->realwanted++; + *buf++ = b; + howmuch--; + } + return toread; +#undef DECOMPRESS_ONE_BYTE +} + + +/*********************************************************************** + * LZSeek (LZ32.@) + */ +LONG WINAPI LZSeek( HFILE fd, LONG off, INT type ) +{ + struct lzstate *lzs; + LONG newwanted; + + TRACE("(%d,%ld,%d)\n",fd,off,type); + /* not compressed? just use normal _llseek() */ + if (!(lzs = GET_LZ_STATE(fd))) return _llseek(fd,off,type); + newwanted = lzs->realwanted; + switch (type) { + case 1: /* SEEK_CUR */ + newwanted += off; + break; + case 2: /* SEEK_END */ + newwanted = lzs->reallength-off; + break; + default:/* SEEK_SET */ + newwanted = off; + break; + } + if (newwanted>lzs->reallength) + return LZERROR_BADVALUE; + if (newwanted<0) + return LZERROR_BADVALUE; + lzs->realwanted = newwanted; + return newwanted; +} + + +/*********************************************************************** + * LZCopy (LZ32.@) + * + * Copies everything from src to dest + * if src is a LZ compressed file, it will be uncompressed. + * will return the number of bytes written to dest or errors. + */ +LONG WINAPI LZCopy( HFILE src, HFILE dest ) +{ + int usedlzinit = 0, ret, wret; + LONG len; + HFILE oldsrc = src, srcfd; + FILETIME filetime; + struct lzstate *lzs; +#define BUFLEN 1000 + BYTE buf[BUFLEN]; + /* we need that weird typedef, for i can't seem to get function pointer + * casts right. (Or they probably just do not like WINAPI in general) + */ + typedef UINT (WINAPI *_readfun)(HFILE,LPVOID,UINT); + + _readfun xread; + + TRACE("(%d,%d)\n",src,dest); + if (!IS_LZ_HANDLE(src)) { + src = LZInit(src); + if ((INT)src <= 0) return 0; + if (src != oldsrc) usedlzinit=1; + } + + /* not compressed? just copy */ + if (!IS_LZ_HANDLE(src)) + xread=_lread; + else + xread=(_readfun)LZRead; + len=0; + while (1) { + ret=xread(src,buf,BUFLEN); + if (ret<=0) { + if (ret==0) + break; + if (ret==-1) + return LZERROR_READ; + return ret; + } + len += ret; + wret = _lwrite(dest,buf,ret); + if (wret!=ret) + return LZERROR_WRITE; + } + + /* Maintain the timestamp of source file to destination file */ + srcfd = (!(lzs = GET_LZ_STATE(src))) ? src : lzs->realfd; + GetFileTime((HANDLE)srcfd, NULL, NULL, &filetime); + SetFileTime((HANDLE)dest, NULL, NULL, &filetime); + + /* close handle */ + if (usedlzinit) + LZClose(src); + return len; +#undef BUFLEN +} + +/* reverses GetExpandedPathname */ +static LPSTR LZEXPAND_MangleName( LPCSTR fn ) +{ + char *p; + char *mfn = HeapAlloc( GetProcessHeap(), 0, strlen(fn) + 3 ); /* "._" and \0 */ + if(mfn == NULL) return NULL; + strcpy( mfn, fn ); + if (!(p = strrchr( mfn, '\\' ))) p = mfn; + if ((p = strchr( p, '.' ))) + { + p++; + if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */ + else p[strlen(p)-1] = '_'; /* replace last character */ + } + else strcat( mfn, "._" ); /* append "._" */ + return mfn; +} + + +/*********************************************************************** + * LZOpenFileA (LZ32.@) + * + * Opens a file. If not compressed, open it as a normal file. + */ +HFILE WINAPI LZOpenFileA( LPSTR fn, LPOFSTRUCT ofs, WORD mode ) +{ + HFILE fd,cfd; + + TRACE("(%s,%p,%d)\n",fn,ofs,mode); + /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */ + fd=OpenFile(fn,ofs,mode); + if (fd==HFILE_ERROR) + { + LPSTR mfn = LZEXPAND_MangleName(fn); + fd = OpenFile(mfn,ofs,mode); + HeapFree( GetProcessHeap(), 0, mfn ); + } + if ((mode&~0x70)!=OF_READ) + return fd; + if (fd==HFILE_ERROR) + return HFILE_ERROR; + cfd=LZInit(fd); + if ((INT)cfd <= 0) return fd; + return cfd; +} + + +/*********************************************************************** + * LZOpenFileW (LZ32.@) + */ +HFILE WINAPI LZOpenFileW( LPWSTR fn, LPOFSTRUCT ofs, WORD mode ) +{ + HFILE ret; + DWORD len = WideCharToMultiByte( CP_ACP, 0, fn, -1, NULL, 0, NULL, NULL ); + LPSTR xfn = HeapAlloc( GetProcessHeap(), 0, len ); + WideCharToMultiByte( CP_ACP, 0, fn, -1, xfn, len, NULL, NULL ); + ret = LZOpenFileA(xfn,ofs,mode); + HeapFree( GetProcessHeap(), 0, xfn ); + return ret; +} + + +/*********************************************************************** + * LZClose (LZ32.@) + */ +void WINAPI LZClose( HFILE fd ) +{ + struct lzstate *lzs; + + TRACE("(%d)\n",fd); + if (!(lzs = GET_LZ_STATE(fd))) _lclose(fd); + else + { + HeapFree( GetProcessHeap(), 0, lzs->get ); + CloseHandle((HANDLE)lzs->realfd); + lzstates[fd - 0x400] = NULL; + HeapFree( GetProcessHeap(), 0, lzs ); + } +} + + +/*********************************************************************** + * CopyLZFile (LZ32.@) + * + * Copy src to dest (including uncompressing src). + * NOTE: Yes. This is exactly the same function as LZCopy. + */ +LONG WINAPI CopyLZFile( HFILE src, HFILE dest ) +{ + TRACE("(%d,%d)\n",src,dest); + return LZCopy(src,dest); +} diff --git a/dlls/lzexpand/lz32.spec b/dlls/lzexpand/lz32.spec index caeea47c16b..9a89a9da4b7 100644 --- a/dlls/lzexpand/lz32.spec +++ b/dlls/lzexpand/lz32.spec @@ -1,12 +1,12 @@ -@ stdcall CopyLZFile(long long) -@ stdcall GetExpandedNameA(str ptr) -@ stdcall GetExpandedNameW(wstr ptr) -@ stdcall LZClose(long) -@ stdcall LZCopy(long long) -@ stdcall LZDone() -@ stdcall LZInit(long) -@ stdcall LZOpenFileA(str ptr long) -@ stdcall LZOpenFileW(wstr ptr long) -@ stdcall LZRead(long ptr long) -@ stdcall LZSeek(long long long) -@ stdcall LZStart() +@ stdcall CopyLZFile(long long) kernel32.CopyLZFile +@ stdcall GetExpandedNameA(str ptr) kernel32.GetExpandedNameA +@ stdcall GetExpandedNameW(wstr ptr) kernel32.GetExpandedNameW +@ stdcall LZClose(long) kernel32.LZClose +@ stdcall LZCopy(long long) kernel32.LZCopy +@ stdcall LZDone() kernel32.LZDone +@ stdcall LZInit(long) kernel32.LZInit +@ stdcall LZOpenFileA(str ptr long) kernel32.LZOpenFileA +@ stdcall LZOpenFileW(wstr ptr long) kernel32.LZOpenFileW +@ stdcall LZRead(long ptr long) kernel32.LZRead +@ stdcall LZSeek(long long long) kernel32.LZSeek +@ stdcall LZStart() kernel32.LZStart diff --git a/dlls/lzexpand/lzexpand_main.c b/dlls/lzexpand/lzexpand_main.c index 8d560d6f41b..355c97a9d8f 100644 --- a/dlls/lzexpand/lzexpand_main.c +++ b/dlls/lzexpand/lzexpand_main.c @@ -1,598 +1 @@ -/* - * LZ Decompression functions - * - * Copyright 1996 Marcus Meissner - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * NOTES - * - * The LZ (Lempel Ziv) decompression was used in win16 installation programs. - * It is a simple tabledriven decompression engine, the algorithm is not - * documented as far as I know. WINE does not contain a compressor for - * this format. - * - * The implementation is complete and there have been no reports of failures - * for some time. - * - * TODO: - * - * o Check whether the return values are correct - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -# include -#endif - -#include "windef.h" -#include "winbase.h" -#include "lzexpand.h" - -#include "wine/unicode.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(file); - -/* The readahead length of the decompressor. Reading single bytes - * using _lread() would be SLOW. - */ -#define GETLEN 2048 - -/* Format of first 14 byte of LZ compressed file */ -struct lzfileheader { - BYTE magic[8]; - BYTE compressiontype; - CHAR lastchar; - DWORD reallength; -}; -static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33}; - -struct lzstate { - HFILE realfd; /* the real filedescriptor */ - CHAR lastchar; /* the last char of the filename */ - - DWORD reallength; /* the decompressed length of the file */ - DWORD realcurrent; /* the position the decompressor currently is */ - DWORD realwanted; /* the position the user wants to read from */ - - BYTE table[0x1000]; /* the rotating LZ table */ - UINT curtabent; /* CURrent TABle ENTry */ - - BYTE stringlen; /* length and position of current string */ - DWORD stringpos; /* from stringtable */ - - - WORD bytetype; /* bitmask within blocks */ - - BYTE *get; /* GETLEN bytes */ - DWORD getcur; /* current read */ - DWORD getlen; /* length last got */ -}; - -#define MAX_LZSTATES 16 -static struct lzstate *lzstates[MAX_LZSTATES]; - -#define IS_LZ_HANDLE(h) (((h) >= 0x400) && ((h) < 0x400+MAX_LZSTATES)) -#define GET_LZ_STATE(h) (IS_LZ_HANDLE(h) ? lzstates[(h)-0x400] : NULL) - -/* reads one compressed byte, including buffering */ -#define GET(lzs,b) _lzget(lzs,&b) -#define GET_FLUSH(lzs) lzs->getcur=lzs->getlen; - -static int -_lzget(struct lzstate *lzs,BYTE *b) { - if (lzs->getcurgetlen) { - *b = lzs->get[lzs->getcur++]; - return 1; - } else { - int ret = _lread(lzs->realfd,lzs->get,GETLEN); - if (ret==HFILE_ERROR) - return HFILE_ERROR; - if (ret==0) - return 0; - lzs->getlen = ret; - lzs->getcur = 1; - *b = *(lzs->get); - return 1; - } -} -/* internal function, reads lzheader - * returns BADINHANDLE for non filedescriptors - * return 0 for file not compressed using LZ - * return UNKNOWNALG for unknown algorithm - * returns lzfileheader in *head - */ -static INT read_header(HFILE fd,struct lzfileheader *head) -{ - BYTE buf[14]; - - if (_llseek(fd,0,SEEK_SET)==-1) - return LZERROR_BADINHANDLE; - - /* We can't directly read the lzfileheader struct due to - * structure element alignment - */ - if (_lread(fd,buf,14)<14) - return 0; - memcpy(head->magic,buf,8); - memcpy(&(head->compressiontype),buf+8,1); - memcpy(&(head->lastchar),buf+9,1); - - /* FIXME: consider endianess on non-intel architectures */ - memcpy(&(head->reallength),buf+10,4); - - if (memcmp(head->magic,LZMagic,8)) - return 0; - if (head->compressiontype!='A') - return LZERROR_UNKNOWNALG; - return 1; -} - - -/*********************************************************************** - * LZStart (LZ32.@) - */ -INT WINAPI LZStart(void) -{ - TRACE("(void)\n"); - return 1; -} - - -/*********************************************************************** - * LZInit (LZ32.@) - * - * initializes internal decompression buffers, returns lzfiledescriptor. - * (return value the same as hfSrc, if hfSrc is not compressed) - * on failure, returns error code <0 - * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process) - * - * since _llseek uses the same types as libc.lseek, we just use the macros of - * libc - */ -HFILE WINAPI LZInit( HFILE hfSrc ) -{ - - struct lzfileheader head; - struct lzstate *lzs; - DWORD ret; - int i; - - TRACE("(%d)\n",hfSrc); - ret=read_header(hfSrc,&head); - if (ret<=0) { - _llseek(hfSrc,0,SEEK_SET); - return ret?ret:hfSrc; - } - for (i = 0; i < MAX_LZSTATES; i++) if (!lzstates[i]) break; - if (i == MAX_LZSTATES) return LZERROR_GLOBALLOC; - lzstates[i] = lzs = HeapAlloc( GetProcessHeap(), 0, sizeof(struct lzstate) ); - if(lzs == NULL) return LZERROR_GLOBALLOC; - - memset(lzs,'\0',sizeof(*lzs)); - lzs->realfd = hfSrc; - lzs->lastchar = head.lastchar; - lzs->reallength = head.reallength; - - lzs->get = HeapAlloc( GetProcessHeap(), 0, GETLEN ); - lzs->getlen = 0; - lzs->getcur = 0; - - if(lzs->get == NULL) { - HeapFree(GetProcessHeap(), 0, lzs); - lzstates[i] = NULL; - return LZERROR_GLOBALLOC; - } - - /* Yes, preinitialize with spaces */ - memset(lzs->table,' ',0x1000); - /* Yes, start 16 byte from the END of the table */ - lzs->curtabent = 0xff0; - return 0x400 + i; -} - - -/*********************************************************************** - * LZDone (LZEXPAND.9) - * LZDone (LZ32.@) - */ -void WINAPI LZDone(void) -{ - TRACE("(void)\n"); -} - - -/*********************************************************************** - * GetExpandedNameA (LZ32.@) - * - * gets the full filename of the compressed file 'in' by opening it - * and reading the header - * - * "file." is being translated to "file" - * "file.bl_" (with lastchar 'a') is being translated to "file.bla" - * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA" - */ - -INT WINAPI GetExpandedNameA( LPSTR in, LPSTR out ) -{ - struct lzfileheader head; - HFILE fd; - OFSTRUCT ofs; - INT fnislowercased,ret,len; - LPSTR s,t; - - TRACE("(%s)\n",in); - fd=OpenFile(in,&ofs,OF_READ); - if (fd==HFILE_ERROR) - return (INT)(INT16)LZERROR_BADINHANDLE; - strcpy(out,in); - ret=read_header(fd,&head); - if (ret<=0) { - /* not a LZ compressed file, so the expanded name is the same - * as the input name */ - _lclose(fd); - return 1; - } - - - /* look for directory prefix and skip it. */ - s=out; - while (NULL!=(t=strpbrk(s,"/\\:"))) - s=t+1; - - /* now mangle the basename */ - if (!*s) { - /* FIXME: hmm. shouldn't happen? */ - WARN("Specified a directory or what? (%s)\n",in); - _lclose(fd); - return 1; - } - /* see if we should use lowercase or uppercase on the last char */ - fnislowercased=1; - t=s+strlen(s)-1; - while (t>=out) { - if (!isalpha(*t)) { - t--; - continue; - } - fnislowercased=islower(*t); - break; - } - if (isalpha(head.lastchar)) { - if (fnislowercased) - head.lastchar=tolower(head.lastchar); - else - head.lastchar=toupper(head.lastchar); - } - - /* now look where to replace the last character */ - if (NULL!=(t=strchr(s,'.'))) { - if (t[1]=='\0') { - t[0]='\0'; - } else { - len=strlen(t)-1; - if (t[len]=='_') - t[len]=head.lastchar; - } - } /* else no modification necessary */ - _lclose(fd); - return 1; -} - - -/*********************************************************************** - * GetExpandedNameW (LZ32.@) - */ -INT WINAPI GetExpandedNameW( LPWSTR in, LPWSTR out ) -{ - INT ret; - DWORD len = WideCharToMultiByte( CP_ACP, 0, in, -1, NULL, 0, NULL, NULL ); - char *xin = HeapAlloc( GetProcessHeap(), 0, len ); - char *xout = HeapAlloc( GetProcessHeap(), 0, len+3 ); - WideCharToMultiByte( CP_ACP, 0, in, -1, xin, len, NULL, NULL ); - if ((ret = GetExpandedNameA( xin, xout )) > 0) - MultiByteToWideChar( CP_ACP, 0, xout, -1, out, strlenW(in)+4 ); - HeapFree( GetProcessHeap(), 0, xin ); - HeapFree( GetProcessHeap(), 0, xout ); - return ret; -} - - -/*********************************************************************** - * LZRead (LZ32.@) - */ -INT WINAPI LZRead( HFILE fd, LPSTR vbuf, INT toread ) -{ - int howmuch; - BYTE b,*buf; - struct lzstate *lzs; - - buf=(LPBYTE)vbuf; - TRACE("(%d,%p,%d)\n",fd,buf,toread); - howmuch=toread; - if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread); - -/* The decompressor itself is in a define, cause we need it twice - * in this function. (the decompressed byte will be in b) - */ -#define DECOMPRESS_ONE_BYTE \ - if (lzs->stringlen) { \ - b = lzs->table[lzs->stringpos]; \ - lzs->stringpos = (lzs->stringpos+1)&0xFFF; \ - lzs->stringlen--; \ - } else { \ - if (!(lzs->bytetype&0x100)) { \ - if (1!=GET(lzs,b)) \ - return toread-howmuch; \ - lzs->bytetype = b|0xFF00; \ - } \ - if (lzs->bytetype & 1) { \ - if (1!=GET(lzs,b)) \ - return toread-howmuch; \ - } else { \ - BYTE b1,b2; \ - \ - if (1!=GET(lzs,b1)) \ - return toread-howmuch; \ - if (1!=GET(lzs,b2)) \ - return toread-howmuch; \ - /* Format: \ - * b1 b2 \ - * AB CD \ - * where CAB is the stringoffset in the table\ - * and D+3 is the len of the string \ - */ \ - lzs->stringpos = b1|((b2&0xf0)<<4); \ - lzs->stringlen = (b2&0xf)+2; \ - /* 3, but we use a byte already below ... */\ - b = lzs->table[lzs->stringpos];\ - lzs->stringpos = (lzs->stringpos+1)&0xFFF;\ - } \ - lzs->bytetype>>=1; \ - } \ - /* store b in table */ \ - lzs->table[lzs->curtabent++]= b; \ - lzs->curtabent &= 0xFFF; \ - lzs->realcurrent++; - - /* if someone has seeked, we have to bring the decompressor - * to that position - */ - if (lzs->realcurrent!=lzs->realwanted) { - /* if the wanted position is before the current position - * I see no easy way to unroll ... We have to restart at - * the beginning. *sigh* - */ - if (lzs->realcurrent>lzs->realwanted) { - /* flush decompressor state */ - _llseek(lzs->realfd,14,SEEK_SET); - GET_FLUSH(lzs); - lzs->realcurrent= 0; - lzs->bytetype = 0; - lzs->stringlen = 0; - memset(lzs->table,' ',0x1000); - lzs->curtabent = 0xFF0; - } - while (lzs->realcurrentrealwanted) { - DECOMPRESS_ONE_BYTE; - } - } - - while (howmuch) { - DECOMPRESS_ONE_BYTE; - lzs->realwanted++; - *buf++ = b; - howmuch--; - } - return toread; -#undef DECOMPRESS_ONE_BYTE -} - - -/*********************************************************************** - * LZSeek (LZ32.@) - */ -LONG WINAPI LZSeek( HFILE fd, LONG off, INT type ) -{ - struct lzstate *lzs; - LONG newwanted; - - TRACE("(%d,%ld,%d)\n",fd,off,type); - /* not compressed? just use normal _llseek() */ - if (!(lzs = GET_LZ_STATE(fd))) return _llseek(fd,off,type); - newwanted = lzs->realwanted; - switch (type) { - case 1: /* SEEK_CUR */ - newwanted += off; - break; - case 2: /* SEEK_END */ - newwanted = lzs->reallength-off; - break; - default:/* SEEK_SET */ - newwanted = off; - break; - } - if (newwanted>lzs->reallength) - return LZERROR_BADVALUE; - if (newwanted<0) - return LZERROR_BADVALUE; - lzs->realwanted = newwanted; - return newwanted; -} - - -/*********************************************************************** - * LZCopy (LZ32.@) - * - * Copies everything from src to dest - * if src is a LZ compressed file, it will be uncompressed. - * will return the number of bytes written to dest or errors. - */ -LONG WINAPI LZCopy( HFILE src, HFILE dest ) -{ - int usedlzinit = 0, ret, wret; - LONG len; - HFILE oldsrc = src, srcfd; - FILETIME filetime; - struct lzstate *lzs; -#define BUFLEN 1000 - BYTE buf[BUFLEN]; - /* we need that weird typedef, for i can't seem to get function pointer - * casts right. (Or they probably just do not like WINAPI in general) - */ - typedef UINT (WINAPI *_readfun)(HFILE,LPVOID,UINT); - - _readfun xread; - - TRACE("(%d,%d)\n",src,dest); - if (!IS_LZ_HANDLE(src)) { - src = LZInit(src); - if ((INT)src <= 0) return 0; - if (src != oldsrc) usedlzinit=1; - } - - /* not compressed? just copy */ - if (!IS_LZ_HANDLE(src)) - xread=_lread; - else - xread=(_readfun)LZRead; - len=0; - while (1) { - ret=xread(src,buf,BUFLEN); - if (ret<=0) { - if (ret==0) - break; - if (ret==-1) - return LZERROR_READ; - return ret; - } - len += ret; - wret = _lwrite(dest,buf,ret); - if (wret!=ret) - return LZERROR_WRITE; - } - - /* Maintain the timestamp of source file to destination file */ - srcfd = (!(lzs = GET_LZ_STATE(src))) ? src : lzs->realfd; - GetFileTime((HANDLE)srcfd, NULL, NULL, &filetime); - SetFileTime((HANDLE)dest, NULL, NULL, &filetime); - - /* close handle */ - if (usedlzinit) - LZClose(src); - return len; -#undef BUFLEN -} - -/* reverses GetExpandedPathname */ -static LPSTR LZEXPAND_MangleName( LPCSTR fn ) -{ - char *p; - char *mfn = HeapAlloc( GetProcessHeap(), 0, strlen(fn) + 3 ); /* "._" and \0 */ - if(mfn == NULL) return NULL; - strcpy( mfn, fn ); - if (!(p = strrchr( mfn, '\\' ))) p = mfn; - if ((p = strchr( p, '.' ))) - { - p++; - if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */ - else p[strlen(p)-1] = '_'; /* replace last character */ - } - else strcat( mfn, "._" ); /* append "._" */ - return mfn; -} - - -/*********************************************************************** - * LZOpenFileA (LZ32.@) - * - * Opens a file. If not compressed, open it as a normal file. - */ -HFILE WINAPI LZOpenFileA( LPSTR fn, LPOFSTRUCT ofs, WORD mode ) -{ - HFILE fd,cfd; - - TRACE("(%s,%p,%d)\n",fn,ofs,mode); - /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */ - fd=OpenFile(fn,ofs,mode); - if (fd==HFILE_ERROR) - { - LPSTR mfn = LZEXPAND_MangleName(fn); - fd = OpenFile(mfn,ofs,mode); - HeapFree( GetProcessHeap(), 0, mfn ); - } - if ((mode&~0x70)!=OF_READ) - return fd; - if (fd==HFILE_ERROR) - return HFILE_ERROR; - cfd=LZInit(fd); - if ((INT)cfd <= 0) return fd; - return cfd; -} - - -/*********************************************************************** - * LZOpenFileW (LZ32.@) - */ -HFILE WINAPI LZOpenFileW( LPWSTR fn, LPOFSTRUCT ofs, WORD mode ) -{ - HFILE ret; - DWORD len = WideCharToMultiByte( CP_ACP, 0, fn, -1, NULL, 0, NULL, NULL ); - LPSTR xfn = HeapAlloc( GetProcessHeap(), 0, len ); - WideCharToMultiByte( CP_ACP, 0, fn, -1, xfn, len, NULL, NULL ); - ret = LZOpenFileA(xfn,ofs,mode); - HeapFree( GetProcessHeap(), 0, xfn ); - return ret; -} - - -/*********************************************************************** - * LZClose (LZ32.@) - */ -void WINAPI LZClose( HFILE fd ) -{ - struct lzstate *lzs; - - TRACE("(%d)\n",fd); - if (!(lzs = GET_LZ_STATE(fd))) _lclose(fd); - else - { - HeapFree( GetProcessHeap(), 0, lzs->get ); - CloseHandle((HANDLE)lzs->realfd); - lzstates[fd - 0x400] = NULL; - HeapFree( GetProcessHeap(), 0, lzs ); - } -} - - -/*********************************************************************** - * CopyLZFile (LZ32.@) - * - * Copy src to dest (including uncompressing src). - * NOTE: Yes. This is exactly the same function as LZCopy. - */ -LONG WINAPI CopyLZFile( HFILE src, HFILE dest ) -{ - TRACE("(%d,%d)\n",src,dest); - return LZCopy(src,dest); -} +/* Nothing here - See lzexpand.c in kernel32 */