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