Release 970215
[wine] / misc / lzexpand.c
1 /*
2  * LZ Decompression functions 
3  *
4  * Copyright 1996 Marcus Meissner
5  */
6 /* 
7  * FIXME: return values might be wrong
8  */
9
10 #define NO_TRANSITION_TYPES  /* This file is Win32-clean */
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include "windows.h"
19 #include "file.h"
20 #include "heap.h"
21 #include "ldt.h"
22 #include "lzexpand.h"
23 #include "stddebug.h"
24 #include "debug.h"
25 #include "xmalloc.h"
26
27
28 /* The readahead length of the decompressor. Reading single bytes
29  * using _lread() would be SLOW.
30  */
31 #define GETLEN  2048
32
33 /* Format of first 14 byte of LZ compressed file */
34 struct lzfileheader {
35         BYTE    magic[8];
36         BYTE    compressiontype;
37         CHAR    lastchar;
38         DWORD   reallength;             
39 };
40 static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
41
42 static struct lzstate {
43         HFILE32 lzfd;           /* the handle used by the program */
44         HFILE32 realfd;         /* the real filedescriptor */
45         CHAR    lastchar;       /* the last char of the filename */
46
47         DWORD   reallength;     /* the decompressed length of the file */
48         DWORD   realcurrent;    /* the position the decompressor currently is */
49         DWORD   realwanted;     /* the position the user wants to read from */
50
51         BYTE    table[0x1000];  /* the rotating LZ table */
52         UINT32  curtabent;      /* CURrent TABle ENTry */
53
54         BYTE    stringlen;      /* length and position of current string */ 
55         DWORD   stringpos;      /* from stringtable */
56
57
58         WORD    bytetype;       /* bitmask within blocks */
59
60         BYTE    *get;           /* GETLEN bytes */
61         DWORD   getcur;         /* current read */
62         DWORD   getlen;         /* length last got */
63 } *lzstates=NULL;
64 static int nroflzstates=0;
65
66 /* reads one compressed byte, including buffering */
67 #define GET(lzs,b)      _lzget(lzs,&b)
68 #define GET_FLUSH(lzs)  lzs->getcur=lzs->getlen;
69
70 static int
71 _lzget(struct lzstate *lzs,BYTE *b) {
72         if (lzs->getcur<lzs->getlen) {
73                 *b              = lzs->get[lzs->getcur++];
74                 return          1;
75         } else {
76                 int ret = _lread32(lzs->realfd,lzs->get,GETLEN);
77                 if (ret==HFILE_ERROR32)
78                         return HFILE_ERROR32;
79                 if (ret==0)
80                         return 0;
81                 lzs->getlen     = ret;
82                 lzs->getcur     = 1;
83                 *b              = *(lzs->get);
84                 return 1;
85         }
86 }
87 /* internal function, reads lzheader
88  * returns BADINHANDLE for non filedescriptors
89  * return 0 for file not compressed using LZ 
90  * return UNKNOWNALG for unknown algorithm
91  * returns lzfileheader in *head
92  */
93 static INT32 read_header(HFILE32 fd,struct lzfileheader *head)
94 {
95         BYTE    buf[14];
96
97         if (_llseek32(fd,0,SEEK_SET)==-1)
98                 return LZERROR_BADINHANDLE;
99
100         /* We can't directly read the lzfileheader struct due to 
101          * structure element alignment
102          */
103         if (_lread32(fd,buf,14)<14)
104                 return 0;
105         memcpy(head->magic,buf,8);
106         memcpy(&(head->compressiontype),buf+8,1);
107         memcpy(&(head->lastchar),buf+9,1);
108
109         /* FIXME: consider endianess on non-intel architectures */
110         memcpy(&(head->reallength),buf+10,4);
111
112         if (memcmp(head->magic,LZMagic,8))
113                 return 0;
114         if (head->compressiontype!='A')
115                 return LZERROR_UNKNOWNALG;
116         return 1;
117 }
118
119 /***********************************************************************
120  *           LZStart16   (LZEXPAND.7)
121  */
122 INT16 LZStart16(void)
123 {
124     dprintf_file(stddeb,"LZStart16(void)\n");
125     return 1;
126 }
127
128
129 /***********************************************************************
130  *           LZStart32   (LZ32.6)
131  */
132 INT32 LZStart32(void)
133 {
134     dprintf_file(stddeb,"LZStart32(void)\n");
135     return 1;
136 }
137
138
139 /***********************************************************************
140  *           LZInit16   (LZEXPAND.3)
141  */
142 HFILE16 LZInit16( HFILE16 hfSrc )
143 {
144     return LZInit32( hfSrc );
145 }
146
147
148 /***********************************************************************
149  *           LZInit32   (LZ32.2)
150  *
151  * initializes internal decompression buffers, returns lzfiledescriptor.
152  * (return value the same as hfSrc, if hfSrc is not compressed)
153  * on failure, returns error code <0
154  * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
155  * we use as much as we need, we just OR 0x400 to the passed HFILE.
156  *
157  * since _llseek uses the same types as libc.lseek, we just use the macros of 
158  *  libc
159  */
160 HFILE32 LZInit32( HFILE32 hfSrc )
161 {
162
163         struct  lzfileheader    head;
164         struct  lzstate         *lzs;
165         DWORD   ret;
166
167         dprintf_file(stddeb,"LZInit(%d)\n",hfSrc);
168         ret=read_header(hfSrc,&head);
169         if (ret<=0) {
170                 _llseek32(hfSrc,0,SEEK_SET);
171                 return ret?ret:hfSrc;
172         }
173         lzstates=xrealloc(lzstates,(++nroflzstates)*sizeof(struct lzstate));
174         lzs             = lzstates+(nroflzstates-1);
175
176         memset(lzs,'\0',sizeof(*lzs));
177         lzs->realfd     = hfSrc;
178         lzs->lzfd       = hfSrc | 0x400;
179         lzs->lastchar   = head.lastchar;
180         lzs->reallength = head.reallength;
181
182         lzs->get        = xmalloc(GETLEN);
183         lzs->getlen     = 0;
184         lzs->getcur     = 0;
185
186         /* Yes, preinitialize with spaces */
187         memset(lzs->table,' ',0x1000);
188         /* Yes, start 16 byte from the END of the table */
189         lzs->curtabent  = 0xff0; 
190         return lzs->lzfd;
191 }
192
193
194 /***********************************************************************
195  *           LZDone   (LZEXPAND.9) (LZ32.8)
196  */
197 void LZDone(void)
198 {
199     dprintf_file(stddeb,"LZDone()\n");
200 }
201
202
203 /***********************************************************************
204  *           GetExpandedName16   (LZEXPAND.10)
205  */
206 INT16 GetExpandedName16( LPCSTR in, LPSTR out )
207 {
208     return GetExpandedName32A( in, out );
209 }
210
211
212 /***********************************************************************
213  *           GetExpandedName32A   (LZ32.9)
214  *
215  * gets the full filename of the compressed file 'in' by opening it
216  * and reading the header
217  *
218  * "file." is being translated to "file"
219  * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
220  * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
221  */
222
223 INT32 GetExpandedName32A( LPCSTR in, LPSTR out )
224 {
225         struct lzfileheader     head;
226         HFILE32         fd;
227         OFSTRUCT        ofs;
228         INT32           fnislowercased,ret,len;
229         LPSTR           s,t;
230
231         dprintf_file(stddeb,"GetExpandedName(%s)\n",in);
232         fd=OpenFile32(in,&ofs,OF_READ);
233         if (fd==HFILE_ERROR32)
234                 return LZERROR_BADINHANDLE;
235         ret=read_header(fd,&head);
236         if (ret<=0) {
237                 _lclose32(fd);
238                 return LZERROR_BADINHANDLE;
239         }
240
241         /* This line will crash if the caller hasn't allocated enough memory
242          * for us.
243          */
244         strcpy(out,in);
245
246         /* look for directory prefix and skip it. */
247         s=out;
248         while (NULL!=(t=strpbrk(s,"/\\:")))
249                 s=t+1;
250
251         /* now mangle the basename */
252         if (!*s) {
253                 /* FIXME: hmm. shouldn't happen? */
254                 fprintf(stddeb,__FILE__":GetExpandedFileName(), specified a directory or what? (%s)\n",in);
255                 _lclose32(fd);
256                 return 1;
257         }
258         /* see if we should use lowercase or uppercase on the last char */
259         fnislowercased=1;
260         t=s+strlen(s)-1;
261         while (t>=out) {
262                 if (!isalpha(*t)) {
263                         t--;
264                         continue;
265                 }
266                 fnislowercased=islower(*t);
267                 break;
268         }
269         if (isalpha(head.lastchar)) {
270                 if (fnislowercased)
271                         head.lastchar=tolower(head.lastchar);
272                 else
273                         head.lastchar=toupper(head.lastchar);
274         }       
275
276         /* now look where to replace the last character */
277         if (NULL!=(t=strchr(s,'.'))) {
278                 if (t[1]=='\0') {
279                         t[0]='\0';
280                 } else {
281                         len=strlen(t)-1;
282                         if (t[len]=='_')
283                                 t[len]=head.lastchar;
284                 }
285         } /* else no modification necessary */
286         _lclose32(fd);
287         return 1;
288 }
289
290
291 /***********************************************************************
292  *           GetExpandedName32W   (LZ32.11)
293  */
294 INT32 GetExpandedName32W( LPCWSTR in, LPWSTR out )
295 {
296         char    *xin,*xout;
297         INT32   ret;
298
299         xout    = HeapAlloc( GetProcessHeap(), 0, lstrlen32W(in)+3 );
300         xin     = HEAP_strdupWtoA( GetProcessHeap(), 0, in );
301         ret     = GetExpandedName16(xin,xout);
302         if (ret>0) lstrcpyAtoW(out,xout);
303         HeapFree( GetProcessHeap(), 0, xin );
304         HeapFree( GetProcessHeap(), 0, xout );
305         return  ret;
306 }
307
308
309 /***********************************************************************
310  *           LZRead16   (LZEXPAND.5)
311  */
312 INT16 LZRead16( HFILE16 fd, LPVOID buf, UINT16 toread )
313 {
314     return LZRead32(fd,buf,toread);
315 }
316
317
318 /***********************************************************************
319  *           LZRead32   (LZ32.4)
320  */
321 INT32 LZRead32( HFILE32 fd, LPVOID vbuf, UINT32 toread )
322 {
323         int     i,howmuch;
324         BYTE    b,*buf;
325         struct  lzstate *lzs;
326
327         buf=(LPBYTE)vbuf;
328         dprintf_file(stddeb,"LZRead32(%d,%p,%d)\n",fd,buf,toread);
329         howmuch=toread;
330         for (i=0;i<nroflzstates;i++)
331                 if (lzstates[i].lzfd==fd)
332                         break;
333         if (i==nroflzstates)
334                 return _lread32(fd,buf,toread);
335         lzs=lzstates+i;
336
337 /* The decompressor itself is in a define, cause we need it twice
338  * in this function. (the decompressed byte will be in b)
339  */
340 #define DECOMPRESS_ONE_BYTE                                             \
341                 if (lzs->stringlen) {                                   \
342                         b               = lzs->table[lzs->stringpos];   \
343                         lzs->stringpos  = (lzs->stringpos+1)&0xFFF;     \
344                         lzs->stringlen--;                               \
345                 } else {                                                \
346                         if (!(lzs->bytetype&0x100)) {                   \
347                                 if (1!=GET(lzs,b))                      \
348                                         return toread-howmuch;          \
349                                 lzs->bytetype = b|0xFF00;               \
350                         }                                               \
351                         if (lzs->bytetype & 1) {                        \
352                                 if (1!=GET(lzs,b))                      \
353                                         return toread-howmuch;          \
354                         } else {                                        \
355                                 BYTE    b1,b2;                          \
356                                                                         \
357                                 if (1!=GET(lzs,b1))                     \
358                                         return toread-howmuch;          \
359                                 if (1!=GET(lzs,b2))                     \
360                                         return toread-howmuch;          \
361                                 /* Format:                              \
362                                  * b1 b2                                \
363                                  * AB CD                                \
364                                  * where CAB is the stringoffset in the table\
365                                  * and D+3 is the len of the string     \
366                                  */                                     \
367                                 lzs->stringpos  = b1|((b2&0xf0)<<4);    \
368                                 lzs->stringlen  = (b2&0xf)+2;           \
369                                 /* 3, but we use a  byte already below ... */\
370                                 b               = lzs->table[lzs->stringpos];\
371                                 lzs->stringpos  = (lzs->stringpos+1)&0xFFF;\
372                         }                                               \
373                         lzs->bytetype>>=1;                              \
374                 }                                                       \
375                 /* store b in table */                                  \
376                 lzs->table[lzs->curtabent++]= b;                        \
377                 lzs->curtabent  &= 0xFFF;                               \
378                 lzs->realcurrent++;
379
380         /* if someone has seeked, we have to bring the decompressor 
381          * to that position
382          */
383         if (lzs->realcurrent!=lzs->realwanted) {
384                 /* if the wanted position is before the current position 
385                  * I see no easy way to unroll ... We have to restart at
386                  * the beginning. *sigh*
387                  */
388                 if (lzs->realcurrent>lzs->realwanted) {
389                         /* flush decompressor state */
390                         _llseek32(lzs->realfd,14,SEEK_SET);
391                         GET_FLUSH(lzs);
392                         lzs->realcurrent= 0;
393                         lzs->bytetype   = 0;
394                         lzs->stringlen  = 0;
395                         memset(lzs->table,' ',0x1000);
396                         lzs->curtabent  = 0xFF0;
397                 }
398                 while (lzs->realcurrent<lzs->realwanted) {
399                         DECOMPRESS_ONE_BYTE;
400                 }
401         }
402
403         while (howmuch) {
404                 DECOMPRESS_ONE_BYTE;
405                 lzs->realwanted++;
406                 *buf++          = b;
407                 howmuch--;
408         }
409         return  toread;
410 #undef DECOMPRESS_ONE_BYTE
411 }
412
413
414 /***********************************************************************
415  *           LZSeek16   (LZEXPAND.4)
416  */
417 LONG LZSeek16( HFILE16 fd, LONG off, INT16 type )
418 {
419     return LZSeek32( fd, off, type );
420 }
421
422
423 /***********************************************************************
424  *           LZSeek32   (LZ32.3)
425  */
426 LONG LZSeek32( HFILE32 fd, LONG off, INT32 type )
427 {
428         int     i;
429         struct  lzstate *lzs;
430         LONG    newwanted;
431
432         dprintf_file(stddeb,"LZSeek(%d,%ld,%d)\n",fd,off,type);
433         for (i=0;i<nroflzstates;i++)
434                 if (lzstates[i].lzfd==fd)
435                         break;
436         /* not compressed? just use normal _llseek() */
437         if (i==nroflzstates)
438                 return _llseek32(fd,off,type);
439         lzs             = lzstates+i;
440         newwanted       = lzs->realwanted;
441         switch (type) {
442         case 1: /* SEEK_CUR */
443                 newwanted      += off;
444                 break;
445         case 2: /* SEEK_END */
446                 newwanted       = lzs->reallength-off;
447                 break;
448         default:/* SEEK_SET */
449                 newwanted       = off;
450                 break;
451         }
452         if (newwanted>lzs->reallength)
453                 return LZERROR_BADVALUE;
454         if (newwanted<0)
455                 return LZERROR_BADVALUE;
456         lzs->realwanted = newwanted;
457         return newwanted;
458 }
459
460
461 /***********************************************************************
462  *           LZCopy16   (LZEXPAND.1)
463  */
464 LONG LZCopy16( HFILE16 src, HFILE16 dest )
465 {
466     return LZCopy32( src, dest );
467 }
468
469
470 /***********************************************************************
471  *           LZCopy32   (LZ32.0)
472  *
473  * Copies everything from src to dest
474  * if src is a LZ compressed file, it will be uncompressed.
475  * will return the number of bytes written to dest or errors.
476  */
477 LONG LZCopy32( HFILE32 src, HFILE32 dest )
478 {
479         int     i,ret,wret;
480         LONG    len;
481 #define BUFLEN  1000
482         BYTE    buf[BUFLEN];
483         INT32   (*xread)(HFILE32,LPVOID,UINT32);
484
485         dprintf_file(stddeb,"LZCopy(%d,%d)\n",src,dest);
486         for (i=0;i<nroflzstates;i++)
487                 if (src==lzstates[i].lzfd)
488                         break;
489
490         /* not compressed? just copy */
491         if (i==nroflzstates)
492                 xread=(INT32(*)(HFILE32,LPVOID,UINT32))_lread32;
493         else
494                 xread=LZRead32;
495         len=0;
496         while (1) {
497                 ret=xread(src,buf,BUFLEN);
498                 if (ret<=0) {
499                         if (ret==0)
500                                 break;
501                         if (ret==-1)
502                                 return LZERROR_READ;
503                         return ret;
504                 }
505                 len    += ret;
506                 wret    = _lwrite32(dest,buf,ret);
507                 if (wret!=ret)
508                         return LZERROR_WRITE;
509         }
510         return len;
511 #undef BUFLEN
512 }
513
514 /* reverses GetExpandedPathname */
515 static LPSTR LZEXPAND_MangleName( LPCSTR fn )
516 {
517     char *p;
518     char *mfn = (char *)xmalloc( strlen(fn) + 3 ); /* "._" and \0 */
519     strcpy( mfn, fn );
520     if (!(p = strrchr( mfn, '\\' ))) p = mfn;
521     if ((p = strchr( p, '.' )))
522     {
523         p++;
524         if (strlen(p) < 3) strcat( p, "_" );  /* append '_' */
525         else p[strlen(p)-1] = '_';  /* replace last character */
526     }
527     else strcat( mfn, "._" );   /* append "._" */
528     return mfn;
529 }
530
531
532 /***********************************************************************
533  *           LZOpenFile16   (LZEXPAND.2)
534  */
535 HFILE16 LZOpenFile16( LPCSTR fn, LPOFSTRUCT ofs, UINT16 mode )
536 {
537     return LZOpenFile32A( fn, ofs, mode );
538 }
539
540
541 /***********************************************************************
542  *           LZOpenFile32A   (LZ32.1)
543  *
544  * Opens a file. If not compressed, open it as a normal file.
545  */
546 HFILE32 LZOpenFile32A( LPCSTR fn, LPOFSTRUCT ofs, UINT32 mode )
547 {
548         HFILE32 fd,cfd;
549
550         dprintf_file(stddeb,"LZOpenFile(%s,%p,%d)\n",fn,ofs,mode);
551         /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
552         fd=OpenFile32(fn,ofs,mode);
553         if (fd==HFILE_ERROR32)
554         {
555             LPSTR mfn = LZEXPAND_MangleName(fn);
556             fd = OpenFile32(mfn,ofs,mode);
557             free( mfn );
558         }
559         if ((mode&~0x70)!=OF_READ)
560                 return fd;
561         if (fd==HFILE_ERROR32)
562                 return HFILE_ERROR32;
563         cfd=LZInit32(fd);
564         if (cfd<=0)
565                 return fd;
566         return cfd;
567 }
568
569
570 /***********************************************************************
571  *           LZOpenFile32W   (LZ32.10)
572  */
573 HFILE32 LZOpenFile32W( LPCWSTR fn, LPOFSTRUCT ofs, UINT32 mode )
574 {
575         LPSTR   xfn;
576         LPWSTR  yfn;
577         HFILE32 ret;
578
579         xfn     = HEAP_strdupWtoA( GetProcessHeap(), 0, fn);
580         ret     = LZOpenFile16(xfn,ofs,mode);
581         HeapFree( GetProcessHeap(), 0, xfn );
582         if (ret!=HFILE_ERROR32) {
583                 /* ofs->szPathName is an array with the OFSTRUCT */
584                 yfn = HEAP_strdupAtoW( GetProcessHeap(), 0, ofs->szPathName );
585                 memcpy(ofs->szPathName,yfn,lstrlen32W(yfn)*2+2);
586                 HeapFree( GetProcessHeap(), 0, yfn );
587         }
588         return  ret;
589 }
590
591
592 /***********************************************************************
593  *           LZClose16   (LZEXPAND.6)
594  */
595 void LZClose16( HFILE16 fd )
596 {
597     return LZClose32( fd );
598 }
599
600
601 /***********************************************************************
602  *           LZClose32   (LZ32.5)
603  */
604 void LZClose32( HFILE32 fd )
605 {
606         int     i;
607
608         dprintf_file(stddeb,"LZClose(%d)\n",fd);
609         for (i=0;i<nroflzstates;i++)
610                 if (lzstates[i].lzfd==fd)
611                         break;
612         if (i==nroflzstates) {
613                 _lclose32(fd);
614                 return;
615         }
616         if (lzstates[i].get)
617                 free(lzstates[i].get);
618         _lclose32(lzstates[i].realfd);
619         memcpy(lzstates+i,lzstates+i+1,sizeof(struct lzstate)*(nroflzstates-i-1));
620         nroflzstates--;
621         lzstates=xrealloc(lzstates,sizeof(struct lzstate)*nroflzstates);
622 }
623
624 /***********************************************************************
625  *           CopyLZFile16   (LZEXPAND.8)
626  */
627 LONG CopyLZFile16( HFILE16 src, HFILE16 dest )
628 {
629     dprintf_file(stddeb,"CopyLZFile16(%d,%d)\n",src,dest);
630     return LZCopy32(src,dest);
631 }
632
633
634 /***********************************************************************
635  *           CopyLZFile32  (LZ32.7)
636  *
637  * Copy src to dest (including uncompressing src).
638  * NOTE: Yes. This is exactly the same function as LZCopy.
639  */
640 LONG CopyLZFile32( HFILE32 src, HFILE32 dest )
641 {
642     dprintf_file(stddeb,"CopyLZFile32(%d,%d)\n",src,dest);
643     return LZCopy32(src,dest);
644 }