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