replaced a DPRINTF by a TRACE
[wine.git] / dlls / lzexpand / lzexpand_main.c
blob7e99234c490a6f6499a0278dab1d73d530b0efe9
1 /*
2 * LZ Decompression functions
4 * Copyright 1996 Marcus Meissner
5 */
6 /*
7 * FIXME: return values might be wrong
8 */
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"
23 DEFAULT_DEBUG_CHANNEL(file);
26 /* The readahead length of the decompressor. Reading single bytes
27 * using _lread() would be SLOW.
29 #define GETLEN 2048
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;
38 static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
40 struct lzstate {
41 HFILE realfd; /* the real filedescriptor */
42 CHAR lastchar; /* the last char of the filename */
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 */
48 BYTE table[0x1000]; /* the rotating LZ table */
49 UINT curtabent; /* CURrent TABle ENTry */
51 BYTE stringlen; /* length and position of current string */
52 DWORD stringpos; /* from stringtable */
55 WORD bytetype; /* bitmask within blocks */
57 BYTE *get; /* GETLEN bytes */
58 DWORD getcur; /* current read */
59 DWORD getlen; /* length last got */
62 #define MAX_LZSTATES 16
63 static struct lzstate *lzstates[MAX_LZSTATES];
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)
68 /* reads one compressed byte, including buffering */
69 #define GET(lzs,b) _lzget(lzs,&b)
70 #define GET_FLUSH(lzs) lzs->getcur=lzs->getlen;
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;
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
95 static INT read_header(HFILE fd,struct lzfileheader *head)
97 BYTE buf[14];
99 if (_llseek(fd,0,SEEK_SET)==-1)
100 return LZERROR_BADINHANDLE;
102 /* We can't directly read the lzfileheader struct due to
103 * structure element alignment
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);
111 /* FIXME: consider endianess on non-intel architectures */
112 memcpy(&(head->reallength),buf+10,4);
114 if (memcmp(head->magic,LZMagic,8))
115 return 0;
116 if (head->compressiontype!='A')
117 return LZERROR_UNKNOWNALG;
118 return 1;
121 /***********************************************************************
122 * LZStart16 (LZEXPAND.7)
124 INT16 WINAPI LZStart16(void)
126 TRACE("(void)\n");
127 return 1;
131 /***********************************************************************
132 * LZStart (LZ32.6)
134 INT WINAPI LZStart(void)
136 TRACE("(void)\n");
137 return 1;
141 /***********************************************************************
142 * LZInit16 (LZEXPAND.3)
144 HFILE16 WINAPI LZInit16( HFILE16 hfSrc )
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;
153 /***********************************************************************
154 * LZInit (LZ32.2)
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)
161 * since _llseek uses the same types as libc.lseek, we just use the macros of
162 * libc
164 HFILE WINAPI LZInit( HFILE hfSrc )
167 struct lzfileheader head;
168 struct lzstate *lzs;
169 DWORD ret;
170 int i;
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;
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 if(lzs == NULL) return LZERROR_GLOBALLOC;
183 memset(lzs,'\0',sizeof(*lzs));
184 lzs->realfd = hfSrc;
185 lzs->lastchar = head.lastchar;
186 lzs->reallength = head.reallength;
188 lzs->get = HeapAlloc( GetProcessHeap(), 0, GETLEN );
189 lzs->getlen = 0;
190 lzs->getcur = 0;
192 if(lzs->get == NULL) {
193 HeapFree(GetProcessHeap(), 0, lzs);
194 lzstates[i] = NULL;
195 return LZERROR_GLOBALLOC;
198 /* Yes, preinitialize with spaces */
199 memset(lzs->table,' ',0x1000);
200 /* Yes, start 16 byte from the END of the table */
201 lzs->curtabent = 0xff0;
202 return 0x400 + i;
206 /***********************************************************************
207 * LZDone (LZEXPAND.9) (LZ32.8)
209 void WINAPI LZDone(void)
211 TRACE("(void)\n");
215 /***********************************************************************
216 * GetExpandedName16 (LZEXPAND.10)
218 INT16 WINAPI GetExpandedName16( LPCSTR in, LPSTR out )
220 return (INT16)GetExpandedNameA( in, out );
224 /***********************************************************************
225 * GetExpandedNameA (LZ32.9)
227 * gets the full filename of the compressed file 'in' by opening it
228 * and reading the header
230 * "file." is being translated to "file"
231 * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
232 * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
235 INT WINAPI GetExpandedNameA( LPCSTR in, LPSTR out )
237 struct lzfileheader head;
238 HFILE fd;
239 OFSTRUCT ofs;
240 INT fnislowercased,ret,len;
241 LPSTR s,t;
243 TRACE("(%s)\n",in);
244 fd=OpenFile(in,&ofs,OF_READ);
245 if (fd==HFILE_ERROR)
246 return (INT)(INT16)LZERROR_BADINHANDLE;
247 strcpy(out,in);
248 ret=read_header(fd,&head);
249 if (ret<=0) {
250 /* not a LZ compressed file, so the expanded name is the same
251 * as the input name */
252 _lclose(fd);
253 return 1;
257 /* look for directory prefix and skip it. */
258 s=out;
259 while (NULL!=(t=strpbrk(s,"/\\:")))
260 s=t+1;
262 /* now mangle the basename */
263 if (!*s) {
264 /* FIXME: hmm. shouldn't happen? */
265 WARN("Specified a directory or what? (%s)\n",in);
266 _lclose(fd);
267 return 1;
269 /* see if we should use lowercase or uppercase on the last char */
270 fnislowercased=1;
271 t=s+strlen(s)-1;
272 while (t>=out) {
273 if (!isalpha(*t)) {
274 t--;
275 continue;
277 fnislowercased=islower(*t);
278 break;
280 if (isalpha(head.lastchar)) {
281 if (fnislowercased)
282 head.lastchar=tolower(head.lastchar);
283 else
284 head.lastchar=toupper(head.lastchar);
287 /* now look where to replace the last character */
288 if (NULL!=(t=strchr(s,'.'))) {
289 if (t[1]=='\0') {
290 t[0]='\0';
291 } else {
292 len=strlen(t)-1;
293 if (t[len]=='_')
294 t[len]=head.lastchar;
296 } /* else no modification necessary */
297 _lclose(fd);
298 return 1;
302 /***********************************************************************
303 * GetExpandedNameW (LZ32.11)
305 INT WINAPI GetExpandedNameW( LPCWSTR in, LPWSTR out )
307 char *xin,*xout;
308 INT ret;
310 xout = HeapAlloc( GetProcessHeap(), 0, lstrlenW(in)+3 );
311 xin = HEAP_strdupWtoA( GetProcessHeap(), 0, in );
312 ret = GetExpandedName16(xin,xout);
313 if (ret>0) lstrcpyAtoW(out,xout);
314 HeapFree( GetProcessHeap(), 0, xin );
315 HeapFree( GetProcessHeap(), 0, xout );
316 return ret;
320 /***********************************************************************
321 * LZRead16 (LZEXPAND.5)
323 INT16 WINAPI LZRead16( HFILE16 fd, LPVOID buf, UINT16 toread )
325 if (IS_LZ_HANDLE(fd)) return LZRead( fd, buf, toread );
326 return _lread16( fd, buf, toread );
330 /***********************************************************************
331 * LZRead (LZ32.4)
333 INT WINAPI LZRead( HFILE fd, LPVOID vbuf, UINT toread )
335 int howmuch;
336 BYTE b,*buf;
337 struct lzstate *lzs;
339 buf=(LPBYTE)vbuf;
340 TRACE("(%d,%p,%d)\n",fd,buf,toread);
341 howmuch=toread;
342 if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread);
344 /* The decompressor itself is in a define, cause we need it twice
345 * in this function. (the decompressed byte will be in b)
347 #define DECOMPRESS_ONE_BYTE \
348 if (lzs->stringlen) { \
349 b = lzs->table[lzs->stringpos]; \
350 lzs->stringpos = (lzs->stringpos+1)&0xFFF; \
351 lzs->stringlen--; \
352 } else { \
353 if (!(lzs->bytetype&0x100)) { \
354 if (1!=GET(lzs,b)) \
355 return toread-howmuch; \
356 lzs->bytetype = b|0xFF00; \
358 if (lzs->bytetype & 1) { \
359 if (1!=GET(lzs,b)) \
360 return toread-howmuch; \
361 } else { \
362 BYTE b1,b2; \
364 if (1!=GET(lzs,b1)) \
365 return toread-howmuch; \
366 if (1!=GET(lzs,b2)) \
367 return toread-howmuch; \
368 /* Format: \
369 * b1 b2 \
370 * AB CD \
371 * where CAB is the stringoffset in the table\
372 * and D+3 is the len of the string \
373 */ \
374 lzs->stringpos = b1|((b2&0xf0)<<4); \
375 lzs->stringlen = (b2&0xf)+2; \
376 /* 3, but we use a byte already below ... */\
377 b = lzs->table[lzs->stringpos];\
378 lzs->stringpos = (lzs->stringpos+1)&0xFFF;\
380 lzs->bytetype>>=1; \
382 /* store b in table */ \
383 lzs->table[lzs->curtabent++]= b; \
384 lzs->curtabent &= 0xFFF; \
385 lzs->realcurrent++;
387 /* if someone has seeked, we have to bring the decompressor
388 * to that position
390 if (lzs->realcurrent!=lzs->realwanted) {
391 /* if the wanted position is before the current position
392 * I see no easy way to unroll ... We have to restart at
393 * the beginning. *sigh*
395 if (lzs->realcurrent>lzs->realwanted) {
396 /* flush decompressor state */
397 _llseek(lzs->realfd,14,SEEK_SET);
398 GET_FLUSH(lzs);
399 lzs->realcurrent= 0;
400 lzs->bytetype = 0;
401 lzs->stringlen = 0;
402 memset(lzs->table,' ',0x1000);
403 lzs->curtabent = 0xFF0;
405 while (lzs->realcurrent<lzs->realwanted) {
406 DECOMPRESS_ONE_BYTE;
410 while (howmuch) {
411 DECOMPRESS_ONE_BYTE;
412 lzs->realwanted++;
413 *buf++ = b;
414 howmuch--;
416 return toread;
417 #undef DECOMPRESS_ONE_BYTE
421 /***********************************************************************
422 * LZSeek16 (LZEXPAND.4)
424 LONG WINAPI LZSeek16( HFILE16 fd, LONG off, INT16 type )
426 if (IS_LZ_HANDLE(fd)) return LZSeek( fd, off, type );
427 return _llseek16( fd, off, type );
431 /***********************************************************************
432 * LZSeek (LZ32.3)
434 LONG WINAPI LZSeek( HFILE fd, LONG off, INT type )
436 struct lzstate *lzs;
437 LONG newwanted;
439 TRACE("(%d,%ld,%d)\n",fd,off,type);
440 /* not compressed? just use normal _llseek() */
441 if (!(lzs = GET_LZ_STATE(fd))) return _llseek(fd,off,type);
442 newwanted = lzs->realwanted;
443 switch (type) {
444 case 1: /* SEEK_CUR */
445 newwanted += off;
446 break;
447 case 2: /* SEEK_END */
448 newwanted = lzs->reallength-off;
449 break;
450 default:/* SEEK_SET */
451 newwanted = off;
452 break;
454 if (newwanted>lzs->reallength)
455 return LZERROR_BADVALUE;
456 if (newwanted<0)
457 return LZERROR_BADVALUE;
458 lzs->realwanted = newwanted;
459 return newwanted;
463 /***********************************************************************
464 * LZCopy16 (LZEXPAND.1)
467 LONG WINAPI LZCopy16( HFILE16 src, HFILE16 dest )
469 /* already a LZ handle? */
470 if (IS_LZ_HANDLE(src)) return LZCopy( src, FILE_GetHandle(dest) );
472 /* no, try to open one */
473 src = LZInit16(src);
474 if ((INT16)src <= 0) return 0;
475 if (IS_LZ_HANDLE(src))
477 LONG ret = LZCopy( src, FILE_GetHandle(dest) );
478 LZClose( src );
479 return ret;
481 /* it was not a compressed file */
482 return LZCopy( FILE_GetHandle(src), FILE_GetHandle(dest) );
486 /***********************************************************************
487 * LZCopy (LZ32.0)
489 * Copies everything from src to dest
490 * if src is a LZ compressed file, it will be uncompressed.
491 * will return the number of bytes written to dest or errors.
493 LONG WINAPI LZCopy( HFILE src, HFILE dest )
495 int usedlzinit=0,ret,wret;
496 LONG len;
497 HFILE oldsrc = src;
498 #define BUFLEN 1000
499 BYTE buf[BUFLEN];
500 /* we need that weird typedef, for i can't seem to get function pointer
501 * casts right. (Or they probably just do not like WINAPI in general)
503 typedef UINT WINAPI (*_readfun)(HFILE,LPVOID,UINT);
505 _readfun xread;
507 TRACE("(%d,%d)\n",src,dest);
508 if (!IS_LZ_HANDLE(src)) {
509 src = LZInit(src);
510 if ((INT)src <= 0) return 0;
511 if (src != oldsrc) usedlzinit=1;
514 /* not compressed? just copy */
515 if (!IS_LZ_HANDLE(src))
516 xread=_lread;
517 else
518 xread=(_readfun)LZRead;
519 len=0;
520 while (1) {
521 ret=xread(src,buf,BUFLEN);
522 if (ret<=0) {
523 if (ret==0)
524 break;
525 if (ret==-1)
526 return LZERROR_READ;
527 return ret;
529 len += ret;
530 wret = _lwrite(dest,buf,ret);
531 if (wret!=ret)
532 return LZERROR_WRITE;
534 if (usedlzinit)
535 LZClose(src);
536 return len;
537 #undef BUFLEN
540 /* reverses GetExpandedPathname */
541 static LPSTR LZEXPAND_MangleName( LPCSTR fn )
543 char *p;
544 char *mfn = (char *)HeapAlloc( GetProcessHeap(), 0,
545 strlen(fn) + 3 ); /* "._" and \0 */
546 if(mfn == NULL) return NULL;
547 strcpy( mfn, fn );
548 if (!(p = strrchr( mfn, '\\' ))) p = mfn;
549 if ((p = strchr( p, '.' )))
551 p++;
552 if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */
553 else p[strlen(p)-1] = '_'; /* replace last character */
555 else strcat( mfn, "._" ); /* append "._" */
556 return mfn;
560 /***********************************************************************
561 * LZOpenFile16 (LZEXPAND.2)
563 HFILE16 WINAPI LZOpenFile16( LPCSTR fn, LPOFSTRUCT ofs, UINT16 mode )
565 HFILE hfret = LZOpenFileA( fn, ofs, mode );
566 /* return errors and LZ handles unmodified */
567 if ((INT)hfret <= 0) return hfret;
568 if (IS_LZ_HANDLE(hfret)) return hfret;
569 /* but allocate a dos handle for 'normal' files */
570 return FILE_AllocDosHandle(hfret);
574 /***********************************************************************
575 * LZOpenFileA (LZ32.1)
577 * Opens a file. If not compressed, open it as a normal file.
579 HFILE WINAPI LZOpenFileA( LPCSTR fn, LPOFSTRUCT ofs, UINT mode )
581 HFILE fd,cfd;
583 TRACE("(%s,%p,%d)\n",fn,ofs,mode);
584 /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
585 fd=OpenFile(fn,ofs,mode);
586 if (fd==HFILE_ERROR)
588 LPSTR mfn = LZEXPAND_MangleName(fn);
589 fd = OpenFile(mfn,ofs,mode);
590 HeapFree( GetProcessHeap(), 0, mfn );
592 if ((mode&~0x70)!=OF_READ)
593 return fd;
594 if (fd==HFILE_ERROR)
595 return HFILE_ERROR;
596 cfd=LZInit(fd);
597 if ((INT)cfd <= 0) return fd;
598 return cfd;
602 /***********************************************************************
603 * LZOpenFileW (LZ32.10)
605 HFILE WINAPI LZOpenFileW( LPCWSTR fn, LPOFSTRUCT ofs, UINT mode )
607 LPSTR xfn;
608 LPWSTR yfn;
609 HFILE ret;
611 xfn = HEAP_strdupWtoA( GetProcessHeap(), 0, fn);
612 ret = LZOpenFile16(xfn,ofs,mode);
613 HeapFree( GetProcessHeap(), 0, xfn );
614 if (ret!=HFILE_ERROR) {
615 /* ofs->szPathName is an array with the OFSTRUCT */
616 yfn = HEAP_strdupAtoW( GetProcessHeap(), 0, ofs->szPathName );
617 memcpy(ofs->szPathName,yfn,lstrlenW(yfn)*2+2);
618 HeapFree( GetProcessHeap(), 0, yfn );
620 return ret;
624 /***********************************************************************
625 * LZClose16 (LZEXPAND.6)
627 void WINAPI LZClose16( HFILE16 fd )
629 if (IS_LZ_HANDLE(fd)) LZClose( fd );
630 else _lclose16( fd );
634 /***********************************************************************
635 * LZClose (LZ32.5)
637 void WINAPI LZClose( HFILE fd )
639 struct lzstate *lzs;
641 TRACE("(%d)\n",fd);
642 if (!(lzs = GET_LZ_STATE(fd))) _lclose(fd);
643 else
645 if (lzs->get) HeapFree( GetProcessHeap(), 0, lzs->get );
646 CloseHandle(lzs->realfd);
647 lzstates[fd - 0x400] = NULL;
648 HeapFree( GetProcessHeap(), 0, lzs );
652 /***********************************************************************
653 * CopyLZFile16 (LZEXPAND.8)
655 LONG WINAPI CopyLZFile16( HFILE16 src, HFILE16 dest )
657 TRACE("(%d,%d)\n",src,dest);
658 return LZCopy16(src,dest);
662 /***********************************************************************
663 * CopyLZFile (LZ32.7)
665 * Copy src to dest (including uncompressing src).
666 * NOTE: Yes. This is exactly the same function as LZCopy.
668 LONG WINAPI CopyLZFile( HFILE src, HFILE dest )
670 TRACE("(%d,%d)\n",src,dest);
671 return LZCopy(src,dest);