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