Release 980201
[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
25
26 /* The readahead length of the decompressor. Reading single bytes
27  * using _lread() would be SLOW.
28  */
29 #define GETLEN  2048
30
31 /* Format of first 14 byte of LZ compressed file */
32 struct lzfileheader {
33         BYTE    magic[8];
34         BYTE    compressiontype;
35         CHAR    lastchar;
36         DWORD   reallength;             
37 };
38 static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
39
40 static struct lzstate {
41         HFILE32 lzfd;           /* the handle used by the program */
42         HFILE32 realfd;         /* the real filedescriptor */
43         CHAR    lastchar;       /* the last char of the filename */
44
45         DWORD   reallength;     /* the decompressed length of the file */
46         DWORD   realcurrent;    /* the position the decompressor currently is */
47         DWORD   realwanted;     /* the position the user wants to read from */
48
49         BYTE    table[0x1000];  /* the rotating LZ table */
50         UINT32  curtabent;      /* CURrent TABle ENTry */
51
52         BYTE    stringlen;      /* length and position of current string */ 
53         DWORD   stringpos;      /* from stringtable */
54
55
56         WORD    bytetype;       /* bitmask within blocks */
57
58         BYTE    *get;           /* GETLEN bytes */
59         DWORD   getcur;         /* current read */
60         DWORD   getlen;         /* length last got */
61 } *lzstates=NULL;
62 static int nroflzstates=0;
63
64 /* reads one compressed byte, including buffering */
65 #define GET(lzs,b)      _lzget(lzs,&b)
66 #define GET_FLUSH(lzs)  lzs->getcur=lzs->getlen;
67
68 static int
69 _lzget(struct lzstate *lzs,BYTE *b) {
70         if (lzs->getcur<lzs->getlen) {
71                 *b              = lzs->get[lzs->getcur++];
72                 return          1;
73         } else {
74                 int ret = _lread32(lzs->realfd,lzs->get,GETLEN);
75                 if (ret==HFILE_ERROR32)
76                         return HFILE_ERROR32;
77                 if (ret==0)
78                         return 0;
79                 lzs->getlen     = ret;
80                 lzs->getcur     = 1;
81                 *b              = *(lzs->get);
82                 return 1;
83         }
84 }
85 /* internal function, reads lzheader
86  * returns BADINHANDLE for non filedescriptors
87  * return 0 for file not compressed using LZ 
88  * return UNKNOWNALG for unknown algorithm
89  * returns lzfileheader in *head
90  */
91 static INT32 read_header(HFILE32 fd,struct lzfileheader *head)
92 {
93         BYTE    buf[14];
94
95         if (_llseek32(fd,0,SEEK_SET)==-1)
96                 return LZERROR_BADINHANDLE;
97
98         /* We can't directly read the lzfileheader struct due to 
99          * structure element alignment
100          */
101         if (_lread32(fd,buf,14)<14)
102                 return 0;
103         memcpy(head->magic,buf,8);
104         memcpy(&(head->compressiontype),buf+8,1);
105         memcpy(&(head->lastchar),buf+9,1);
106
107         /* FIXME: consider endianess on non-intel architectures */
108         memcpy(&(head->reallength),buf+10,4);
109
110         if (memcmp(head->magic,LZMagic,8))
111                 return 0;
112         if (head->compressiontype!='A')
113                 return LZERROR_UNKNOWNALG;
114         return 1;
115 }
116
117 /***********************************************************************
118  *           LZStart16   (LZEXPAND.7)
119  */
120 INT16 WINAPI LZStart16(void)
121 {
122     dprintf_file(stddeb,"LZStart16(void)\n");
123     return 1;
124 }
125
126
127 /***********************************************************************
128  *           LZStart32   (LZ32.6)
129  */
130 INT32 WINAPI LZStart32(void)
131 {
132     dprintf_file(stddeb,"LZStart32(void)\n");
133     return 1;
134 }
135
136
137 /***********************************************************************
138  *           LZInit16   (LZEXPAND.3)
139  */
140 HFILE16 WINAPI LZInit16( HFILE16 hfSrc )
141 {
142     return LZInit32( hfSrc );
143 }
144
145
146 /***********************************************************************
147  *           LZInit32   (LZ32.2)
148  *
149  * initializes internal decompression buffers, returns lzfiledescriptor.
150  * (return value the same as hfSrc, if hfSrc is not compressed)
151  * on failure, returns error code <0
152  * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
153  * we use as much as we need, we just OR 0x400 to the passed HFILE.
154  *
155  * since _llseek uses the same types as libc.lseek, we just use the macros of 
156  *  libc
157  */
158 HFILE32 WINAPI LZInit32( HFILE32 hfSrc )
159 {
160
161         struct  lzfileheader    head;
162         struct  lzstate         *lzs;
163         DWORD   ret;
164
165         dprintf_file(stddeb,"LZInit(%d)\n",hfSrc);
166         ret=read_header(hfSrc,&head);
167         if (ret<=0) {
168                 _llseek32(hfSrc,0,SEEK_SET);
169                 return ret?ret:hfSrc;
170         }
171         lzstates = HeapReAlloc( SystemHeap, 0, lzstates,
172                                 (++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        = HEAP_xalloc( GetProcessHeap(), 0, 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 WINAPI LZDone(void)
197 {
198     dprintf_file(stddeb,"LZDone()\n");
199 }
200
201
202 /***********************************************************************
203  *           GetExpandedName16   (LZEXPAND.10)
204  */
205 INT16 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI 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 WINAPI LZCopy32( HFILE32 src, HFILE32 dest )
476 {
477         int     i,ret,wret;
478         LONG    len;
479 #define BUFLEN  1000
480         BYTE    buf[BUFLEN];
481         INT32   WINAPI (*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 *)HEAP_xalloc( GetProcessHeap(), 0,
517                                      strlen(fn) + 3 ); /* "._" and \0 */
518     strcpy( mfn, fn );
519     if (!(p = strrchr( mfn, '\\' ))) p = mfn;
520     if ((p = strchr( p, '.' )))
521     {
522         p++;
523         if (strlen(p) < 3) strcat( p, "_" );  /* append '_' */
524         else p[strlen(p)-1] = '_';  /* replace last character */
525     }
526     else strcat( mfn, "._" );   /* append "._" */
527     return mfn;
528 }
529
530
531 /***********************************************************************
532  *           LZOpenFile16   (LZEXPAND.2)
533  */
534 HFILE16 WINAPI LZOpenFile16( LPCSTR fn, LPOFSTRUCT ofs, UINT16 mode )
535 {
536     return LZOpenFile32A( fn, ofs, mode );
537 }
538
539
540 /***********************************************************************
541  *           LZOpenFile32A   (LZ32.1)
542  *
543  * Opens a file. If not compressed, open it as a normal file.
544  */
545 HFILE32 WINAPI LZOpenFile32A( LPCSTR fn, LPOFSTRUCT ofs, UINT32 mode )
546 {
547         HFILE32 fd,cfd;
548
549         dprintf_file(stddeb,"LZOpenFile(%s,%p,%d)\n",fn,ofs,mode);
550         /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
551         fd=OpenFile32(fn,ofs,mode);
552         if (fd==HFILE_ERROR32)
553         {
554             LPSTR mfn = LZEXPAND_MangleName(fn);
555             fd = OpenFile32(mfn,ofs,mode);
556             HeapFree( GetProcessHeap(), 0, mfn );
557         }
558         if ((mode&~0x70)!=OF_READ)
559                 return fd;
560         if (fd==HFILE_ERROR32)
561                 return HFILE_ERROR32;
562         cfd=LZInit32(fd);
563         if (cfd<=0)
564                 return fd;
565         return cfd;
566 }
567
568
569 /***********************************************************************
570  *           LZOpenFile32W   (LZ32.10)
571  */
572 HFILE32 WINAPI LZOpenFile32W( LPCWSTR fn, LPOFSTRUCT ofs, UINT32 mode )
573 {
574         LPSTR   xfn;
575         LPWSTR  yfn;
576         HFILE32 ret;
577
578         xfn     = HEAP_strdupWtoA( GetProcessHeap(), 0, fn);
579         ret     = LZOpenFile16(xfn,ofs,mode);
580         HeapFree( GetProcessHeap(), 0, xfn );
581         if (ret!=HFILE_ERROR32) {
582                 /* ofs->szPathName is an array with the OFSTRUCT */
583                 yfn = HEAP_strdupAtoW( GetProcessHeap(), 0, ofs->szPathName );
584                 memcpy(ofs->szPathName,yfn,lstrlen32W(yfn)*2+2);
585                 HeapFree( GetProcessHeap(), 0, yfn );
586         }
587         return  ret;
588 }
589
590
591 /***********************************************************************
592  *           LZClose16   (LZEXPAND.6)
593  */
594 void WINAPI LZClose16( HFILE16 fd )
595 {
596     return LZClose32( fd );
597 }
598
599
600 /***********************************************************************
601  *           LZClose32   (LZ32.5)
602  */
603 void WINAPI LZClose32( HFILE32 fd )
604 {
605         int     i;
606
607         dprintf_file(stddeb,"LZClose(%d)\n",fd);
608         for (i=0;i<nroflzstates;i++)
609                 if (lzstates[i].lzfd==fd)
610                         break;
611         if (i==nroflzstates) {
612                 _lclose32(fd);
613                 return;
614         }
615         if (lzstates[i].get)
616                 HeapFree( GetProcessHeap(), 0, lzstates[i].get );
617         _lclose32(lzstates[i].realfd);
618         memmove(lzstates+i,lzstates+i+1,
619                 sizeof(struct lzstate)*(nroflzstates-i-1));
620         nroflzstates--;
621         lzstates = HeapReAlloc( SystemHeap, 0, lzstates,
622                                 sizeof(struct lzstate)*nroflzstates );
623 }
624
625 /***********************************************************************
626  *           CopyLZFile16   (LZEXPAND.8)
627  */
628 LONG WINAPI CopyLZFile16( HFILE16 src, HFILE16 dest )
629 {
630     dprintf_file(stddeb,"CopyLZFile16(%d,%d)\n",src,dest);
631     return LZCopy32(src,dest);
632 }
633
634
635 /***********************************************************************
636  *           CopyLZFile32  (LZ32.7)
637  *
638  * Copy src to dest (including uncompressing src).
639  * NOTE: Yes. This is exactly the same function as LZCopy.
640  */
641 LONG WINAPI CopyLZFile32( HFILE32 src, HFILE32 dest )
642 {
643     dprintf_file(stddeb,"CopyLZFile32(%d,%d)\n",src,dest);
644     return LZCopy32(src,dest);
645 }