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