Display thread id instead of %fs in relay trace.
[wine.git] / dlls / lzexpand / lzexpand_main.c
blobbbfd2cb69f08c5dcf384c559dfb1a71944ceb0c9
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/unicode.h"
18 #include "lzexpand.h"
19 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(file);
23 LONG WINAPI CopyLZFile16(HFILE16,HFILE16);
24 INT16 WINAPI GetExpandedName16(LPCSTR,LPSTR);
25 void WINAPI LZClose16(HFILE16);
26 LONG WINAPI LZCopy16(HFILE16,HFILE16);
27 HFILE16 WINAPI LZInit16(HFILE16);
28 HFILE16 WINAPI LZOpenFile16(LPCSTR,LPOFSTRUCT,UINT16);
29 INT16 WINAPI LZRead16(HFILE16,LPVOID,UINT16);
30 LONG WINAPI LZSeek16(HFILE16,LONG,INT16);
31 INT16 WINAPI LZStart16(void);
33 /* The readahead length of the decompressor. Reading single bytes
34 * using _lread() would be SLOW.
36 #define GETLEN 2048
38 /* Format of first 14 byte of LZ compressed file */
39 struct lzfileheader {
40 BYTE magic[8];
41 BYTE compressiontype;
42 CHAR lastchar;
43 DWORD reallength;
45 static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
47 struct lzstate {
48 HFILE realfd; /* the real filedescriptor */
49 CHAR lastchar; /* the last char of the filename */
51 DWORD reallength; /* the decompressed length of the file */
52 DWORD realcurrent; /* the position the decompressor currently is */
53 DWORD realwanted; /* the position the user wants to read from */
55 BYTE table[0x1000]; /* the rotating LZ table */
56 UINT curtabent; /* CURrent TABle ENTry */
58 BYTE stringlen; /* length and position of current string */
59 DWORD stringpos; /* from stringtable */
62 WORD bytetype; /* bitmask within blocks */
64 BYTE *get; /* GETLEN bytes */
65 DWORD getcur; /* current read */
66 DWORD getlen; /* length last got */
69 #define MAX_LZSTATES 16
70 static struct lzstate *lzstates[MAX_LZSTATES];
72 #define IS_LZ_HANDLE(h) (((h) >= 0x400) && ((h) < 0x400+MAX_LZSTATES))
73 #define GET_LZ_STATE(h) (IS_LZ_HANDLE(h) ? lzstates[(h)-0x400] : NULL)
75 /* reads one compressed byte, including buffering */
76 #define GET(lzs,b) _lzget(lzs,&b)
77 #define GET_FLUSH(lzs) lzs->getcur=lzs->getlen;
79 static int
80 _lzget(struct lzstate *lzs,BYTE *b) {
81 if (lzs->getcur<lzs->getlen) {
82 *b = lzs->get[lzs->getcur++];
83 return 1;
84 } else {
85 int ret = _lread(lzs->realfd,lzs->get,GETLEN);
86 if (ret==HFILE_ERROR)
87 return HFILE_ERROR;
88 if (ret==0)
89 return 0;
90 lzs->getlen = ret;
91 lzs->getcur = 1;
92 *b = *(lzs->get);
93 return 1;
96 /* internal function, reads lzheader
97 * returns BADINHANDLE for non filedescriptors
98 * return 0 for file not compressed using LZ
99 * return UNKNOWNALG for unknown algorithm
100 * returns lzfileheader in *head
102 static INT read_header(HFILE fd,struct lzfileheader *head)
104 BYTE buf[14];
106 if (_llseek(fd,0,SEEK_SET)==-1)
107 return LZERROR_BADINHANDLE;
109 /* We can't directly read the lzfileheader struct due to
110 * structure element alignment
112 if (_lread(fd,buf,14)<14)
113 return 0;
114 memcpy(head->magic,buf,8);
115 memcpy(&(head->compressiontype),buf+8,1);
116 memcpy(&(head->lastchar),buf+9,1);
118 /* FIXME: consider endianess on non-intel architectures */
119 memcpy(&(head->reallength),buf+10,4);
121 if (memcmp(head->magic,LZMagic,8))
122 return 0;
123 if (head->compressiontype!='A')
124 return LZERROR_UNKNOWNALG;
125 return 1;
128 /***********************************************************************
129 * LZStart16 (LZEXPAND.7)
131 INT16 WINAPI LZStart16(void)
133 TRACE("(void)\n");
134 return 1;
138 /***********************************************************************
139 * LZStart (LZ32.6)
141 INT WINAPI LZStart(void)
143 TRACE("(void)\n");
144 return 1;
148 /***********************************************************************
149 * LZInit16 (LZEXPAND.3)
151 HFILE16 WINAPI LZInit16( HFILE16 hfSrc )
153 HFILE ret = LZInit( DosFileHandleToWin32Handle(hfSrc) );
154 if (IS_LZ_HANDLE(ret)) return ret;
155 if ((INT)ret <= 0) return ret;
156 return hfSrc;
160 /***********************************************************************
161 * LZInit (LZ32.2)
163 * initializes internal decompression buffers, returns lzfiledescriptor.
164 * (return value the same as hfSrc, if hfSrc is not compressed)
165 * on failure, returns error code <0
166 * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
168 * since _llseek uses the same types as libc.lseek, we just use the macros of
169 * libc
171 HFILE WINAPI LZInit( HFILE hfSrc )
174 struct lzfileheader head;
175 struct lzstate *lzs;
176 DWORD ret;
177 int i;
179 TRACE("(%d)\n",hfSrc);
180 ret=read_header(hfSrc,&head);
181 if (ret<=0) {
182 _llseek(hfSrc,0,SEEK_SET);
183 return ret?ret:hfSrc;
185 for (i = 0; i < MAX_LZSTATES; i++) if (!lzstates[i]) break;
186 if (i == MAX_LZSTATES) return LZERROR_GLOBALLOC;
187 lzstates[i] = lzs = HeapAlloc( GetProcessHeap(), 0, sizeof(struct lzstate) );
188 if(lzs == NULL) return LZERROR_GLOBALLOC;
190 memset(lzs,'\0',sizeof(*lzs));
191 lzs->realfd = hfSrc;
192 lzs->lastchar = head.lastchar;
193 lzs->reallength = head.reallength;
195 lzs->get = HeapAlloc( GetProcessHeap(), 0, GETLEN );
196 lzs->getlen = 0;
197 lzs->getcur = 0;
199 if(lzs->get == NULL) {
200 HeapFree(GetProcessHeap(), 0, lzs);
201 lzstates[i] = NULL;
202 return LZERROR_GLOBALLOC;
205 /* Yes, preinitialize with spaces */
206 memset(lzs->table,' ',0x1000);
207 /* Yes, start 16 byte from the END of the table */
208 lzs->curtabent = 0xff0;
209 return 0x400 + i;
213 /***********************************************************************
214 * LZDone (LZEXPAND.9) (LZ32.8)
216 void WINAPI LZDone(void)
218 TRACE("(void)\n");
222 /***********************************************************************
223 * GetExpandedName16 (LZEXPAND.10)
225 INT16 WINAPI GetExpandedName16( LPCSTR in, LPSTR out )
227 return (INT16)GetExpandedNameA( in, out );
231 /***********************************************************************
232 * GetExpandedNameA (LZ32.9)
234 * gets the full filename of the compressed file 'in' by opening it
235 * and reading the header
237 * "file." is being translated to "file"
238 * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
239 * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
242 INT WINAPI GetExpandedNameA( LPCSTR in, LPSTR out )
244 struct lzfileheader head;
245 HFILE fd;
246 OFSTRUCT ofs;
247 INT fnislowercased,ret,len;
248 LPSTR s,t;
250 TRACE("(%s)\n",in);
251 fd=OpenFile(in,&ofs,OF_READ);
252 if (fd==HFILE_ERROR)
253 return (INT)(INT16)LZERROR_BADINHANDLE;
254 strcpy(out,in);
255 ret=read_header(fd,&head);
256 if (ret<=0) {
257 /* not a LZ compressed file, so the expanded name is the same
258 * as the input name */
259 _lclose(fd);
260 return 1;
264 /* look for directory prefix and skip it. */
265 s=out;
266 while (NULL!=(t=strpbrk(s,"/\\:")))
267 s=t+1;
269 /* now mangle the basename */
270 if (!*s) {
271 /* FIXME: hmm. shouldn't happen? */
272 WARN("Specified a directory or what? (%s)\n",in);
273 _lclose(fd);
274 return 1;
276 /* see if we should use lowercase or uppercase on the last char */
277 fnislowercased=1;
278 t=s+strlen(s)-1;
279 while (t>=out) {
280 if (!isalpha(*t)) {
281 t--;
282 continue;
284 fnislowercased=islower(*t);
285 break;
287 if (isalpha(head.lastchar)) {
288 if (fnislowercased)
289 head.lastchar=tolower(head.lastchar);
290 else
291 head.lastchar=toupper(head.lastchar);
294 /* now look where to replace the last character */
295 if (NULL!=(t=strchr(s,'.'))) {
296 if (t[1]=='\0') {
297 t[0]='\0';
298 } else {
299 len=strlen(t)-1;
300 if (t[len]=='_')
301 t[len]=head.lastchar;
303 } /* else no modification necessary */
304 _lclose(fd);
305 return 1;
309 /***********************************************************************
310 * GetExpandedNameW (LZ32.11)
312 INT WINAPI GetExpandedNameW( LPCWSTR in, LPWSTR out )
314 INT ret;
315 DWORD len = WideCharToMultiByte( CP_ACP, 0, in, -1, NULL, 0, NULL, NULL );
316 char *xin = HeapAlloc( GetProcessHeap(), 0, len );
317 char *xout = HeapAlloc( GetProcessHeap(), 0, len+3 );
318 WideCharToMultiByte( CP_ACP, 0, in, -1, xin, len, NULL, NULL );
319 if ((ret = GetExpandedNameA( xin, xout )) > 0)
320 MultiByteToWideChar( CP_ACP, 0, xout, -1, out, strlenW(in)+4 );
321 HeapFree( GetProcessHeap(), 0, xin );
322 HeapFree( GetProcessHeap(), 0, xout );
323 return ret;
327 /***********************************************************************
328 * LZRead16 (LZEXPAND.5)
330 INT16 WINAPI LZRead16( HFILE16 fd, LPVOID buf, UINT16 toread )
332 if (IS_LZ_HANDLE(fd)) return LZRead( fd, buf, toread );
333 return _lread( DosFileHandleToWin32Handle(fd), buf, toread );
337 /***********************************************************************
338 * LZRead (LZ32.4)
340 INT WINAPI LZRead( HFILE fd, LPVOID vbuf, UINT toread )
342 int howmuch;
343 BYTE b,*buf;
344 struct lzstate *lzs;
346 buf=(LPBYTE)vbuf;
347 TRACE("(%d,%p,%d)\n",fd,buf,toread);
348 howmuch=toread;
349 if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread);
351 /* The decompressor itself is in a define, cause we need it twice
352 * in this function. (the decompressed byte will be in b)
354 #define DECOMPRESS_ONE_BYTE \
355 if (lzs->stringlen) { \
356 b = lzs->table[lzs->stringpos]; \
357 lzs->stringpos = (lzs->stringpos+1)&0xFFF; \
358 lzs->stringlen--; \
359 } else { \
360 if (!(lzs->bytetype&0x100)) { \
361 if (1!=GET(lzs,b)) \
362 return toread-howmuch; \
363 lzs->bytetype = b|0xFF00; \
365 if (lzs->bytetype & 1) { \
366 if (1!=GET(lzs,b)) \
367 return toread-howmuch; \
368 } else { \
369 BYTE b1,b2; \
371 if (1!=GET(lzs,b1)) \
372 return toread-howmuch; \
373 if (1!=GET(lzs,b2)) \
374 return toread-howmuch; \
375 /* Format: \
376 * b1 b2 \
377 * AB CD \
378 * where CAB is the stringoffset in the table\
379 * and D+3 is the len of the string \
380 */ \
381 lzs->stringpos = b1|((b2&0xf0)<<4); \
382 lzs->stringlen = (b2&0xf)+2; \
383 /* 3, but we use a byte already below ... */\
384 b = lzs->table[lzs->stringpos];\
385 lzs->stringpos = (lzs->stringpos+1)&0xFFF;\
387 lzs->bytetype>>=1; \
389 /* store b in table */ \
390 lzs->table[lzs->curtabent++]= b; \
391 lzs->curtabent &= 0xFFF; \
392 lzs->realcurrent++;
394 /* if someone has seeked, we have to bring the decompressor
395 * to that position
397 if (lzs->realcurrent!=lzs->realwanted) {
398 /* if the wanted position is before the current position
399 * I see no easy way to unroll ... We have to restart at
400 * the beginning. *sigh*
402 if (lzs->realcurrent>lzs->realwanted) {
403 /* flush decompressor state */
404 _llseek(lzs->realfd,14,SEEK_SET);
405 GET_FLUSH(lzs);
406 lzs->realcurrent= 0;
407 lzs->bytetype = 0;
408 lzs->stringlen = 0;
409 memset(lzs->table,' ',0x1000);
410 lzs->curtabent = 0xFF0;
412 while (lzs->realcurrent<lzs->realwanted) {
413 DECOMPRESS_ONE_BYTE;
417 while (howmuch) {
418 DECOMPRESS_ONE_BYTE;
419 lzs->realwanted++;
420 *buf++ = b;
421 howmuch--;
423 return toread;
424 #undef DECOMPRESS_ONE_BYTE
428 /***********************************************************************
429 * LZSeek16 (LZEXPAND.4)
431 LONG WINAPI LZSeek16( HFILE16 fd, LONG off, INT16 type )
433 if (IS_LZ_HANDLE(fd)) return LZSeek( fd, off, type );
434 return _llseek( DosFileHandleToWin32Handle(fd), off, type );
438 /***********************************************************************
439 * LZSeek (LZ32.3)
441 LONG WINAPI LZSeek( HFILE fd, LONG off, INT type )
443 struct lzstate *lzs;
444 LONG newwanted;
446 TRACE("(%d,%ld,%d)\n",fd,off,type);
447 /* not compressed? just use normal _llseek() */
448 if (!(lzs = GET_LZ_STATE(fd))) return _llseek(fd,off,type);
449 newwanted = lzs->realwanted;
450 switch (type) {
451 case 1: /* SEEK_CUR */
452 newwanted += off;
453 break;
454 case 2: /* SEEK_END */
455 newwanted = lzs->reallength-off;
456 break;
457 default:/* SEEK_SET */
458 newwanted = off;
459 break;
461 if (newwanted>lzs->reallength)
462 return LZERROR_BADVALUE;
463 if (newwanted<0)
464 return LZERROR_BADVALUE;
465 lzs->realwanted = newwanted;
466 return newwanted;
470 /***********************************************************************
471 * LZCopy16 (LZEXPAND.1)
474 LONG WINAPI LZCopy16( HFILE16 src, HFILE16 dest )
476 /* already a LZ handle? */
477 if (IS_LZ_HANDLE(src)) return LZCopy( src, DosFileHandleToWin32Handle(dest) );
479 /* no, try to open one */
480 src = LZInit16(src);
481 if ((INT16)src <= 0) return 0;
482 if (IS_LZ_HANDLE(src))
484 LONG ret = LZCopy( src, DosFileHandleToWin32Handle(dest) );
485 LZClose( src );
486 return ret;
488 /* it was not a compressed file */
489 return LZCopy( DosFileHandleToWin32Handle(src), DosFileHandleToWin32Handle(dest) );
493 /***********************************************************************
494 * LZCopy (LZ32.0)
496 * Copies everything from src to dest
497 * if src is a LZ compressed file, it will be uncompressed.
498 * will return the number of bytes written to dest or errors.
500 LONG WINAPI LZCopy( HFILE src, HFILE dest )
502 int usedlzinit=0,ret,wret;
503 LONG len;
504 HFILE oldsrc = src;
505 #define BUFLEN 1000
506 BYTE buf[BUFLEN];
507 /* we need that weird typedef, for i can't seem to get function pointer
508 * casts right. (Or they probably just do not like WINAPI in general)
510 typedef UINT WINAPI (*_readfun)(HFILE,LPVOID,UINT);
512 _readfun xread;
514 TRACE("(%d,%d)\n",src,dest);
515 if (!IS_LZ_HANDLE(src)) {
516 src = LZInit(src);
517 if ((INT)src <= 0) return 0;
518 if (src != oldsrc) usedlzinit=1;
521 /* not compressed? just copy */
522 if (!IS_LZ_HANDLE(src))
523 xread=_lread;
524 else
525 xread=(_readfun)LZRead;
526 len=0;
527 while (1) {
528 ret=xread(src,buf,BUFLEN);
529 if (ret<=0) {
530 if (ret==0)
531 break;
532 if (ret==-1)
533 return LZERROR_READ;
534 return ret;
536 len += ret;
537 wret = _lwrite(dest,buf,ret);
538 if (wret!=ret)
539 return LZERROR_WRITE;
541 if (usedlzinit)
542 LZClose(src);
543 return len;
544 #undef BUFLEN
547 /* reverses GetExpandedPathname */
548 static LPSTR LZEXPAND_MangleName( LPCSTR fn )
550 char *p;
551 char *mfn = (char *)HeapAlloc( GetProcessHeap(), 0,
552 strlen(fn) + 3 ); /* "._" and \0 */
553 if(mfn == NULL) return NULL;
554 strcpy( mfn, fn );
555 if (!(p = strrchr( mfn, '\\' ))) p = mfn;
556 if ((p = strchr( p, '.' )))
558 p++;
559 if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */
560 else p[strlen(p)-1] = '_'; /* replace last character */
562 else strcat( mfn, "._" ); /* append "._" */
563 return mfn;
567 /***********************************************************************
568 * LZOpenFile16 (LZEXPAND.2)
570 HFILE16 WINAPI LZOpenFile16( LPCSTR fn, LPOFSTRUCT ofs, UINT16 mode )
572 HFILE hfret = LZOpenFileA( fn, ofs, mode );
573 /* return errors and LZ handles unmodified */
574 if ((INT)hfret <= 0) return hfret;
575 if (IS_LZ_HANDLE(hfret)) return hfret;
576 /* but allocate a dos handle for 'normal' files */
577 return Win32HandleToDosFileHandle(hfret);
581 /***********************************************************************
582 * LZOpenFileA (LZ32.1)
584 * Opens a file. If not compressed, open it as a normal file.
586 HFILE WINAPI LZOpenFileA( LPCSTR fn, LPOFSTRUCT ofs, UINT mode )
588 HFILE fd,cfd;
590 TRACE("(%s,%p,%d)\n",fn,ofs,mode);
591 /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
592 fd=OpenFile(fn,ofs,mode);
593 if (fd==HFILE_ERROR)
595 LPSTR mfn = LZEXPAND_MangleName(fn);
596 fd = OpenFile(mfn,ofs,mode);
597 HeapFree( GetProcessHeap(), 0, mfn );
599 if ((mode&~0x70)!=OF_READ)
600 return fd;
601 if (fd==HFILE_ERROR)
602 return HFILE_ERROR;
603 cfd=LZInit(fd);
604 if ((INT)cfd <= 0) return fd;
605 return cfd;
609 /***********************************************************************
610 * LZOpenFileW (LZ32.10)
612 HFILE WINAPI LZOpenFileW( LPCWSTR fn, LPOFSTRUCT ofs, UINT mode )
614 HFILE ret;
615 DWORD len = WideCharToMultiByte( CP_ACP, 0, fn, -1, NULL, 0, NULL, NULL );
616 LPSTR xfn = HeapAlloc( GetProcessHeap(), 0, len );
617 WideCharToMultiByte( CP_ACP, 0, fn, -1, xfn, len, NULL, NULL );
618 ret = LZOpenFileA(xfn,ofs,mode);
619 HeapFree( GetProcessHeap(), 0, xfn );
620 return ret;
624 /***********************************************************************
625 * LZClose16 (LZEXPAND.6)
627 void WINAPI LZClose16( HFILE16 fd )
629 if (IS_LZ_HANDLE(fd)) LZClose( fd );
630 else DisposeLZ32Handle( DosFileHandleToWin32Handle(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);