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