2 * LZ Decompression functions
4 * Copyright 1996 Marcus Meissner
7 * FIXME: return values might be wrong
12 #include <sys/types.h>
16 #include "wine/winbase16.h"
17 #include "wine/unicode.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.
38 /* Format of first 14 byte of LZ compressed file */
45 static BYTE LZMagic
[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
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;
80 _lzget(struct lzstate
*lzs
,BYTE
*b
) {
81 if (lzs
->getcur
<lzs
->getlen
) {
82 *b
= lzs
->get
[lzs
->getcur
++];
85 int ret
= _lread(lzs
->realfd
,lzs
->get
,GETLEN
);
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
)
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)
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))
123 if (head
->compressiontype
!='A')
124 return LZERROR_UNKNOWNALG
;
128 /***********************************************************************
129 * LZStart16 (LZEXPAND.7)
131 INT16 WINAPI
LZStart16(void)
138 /***********************************************************************
141 INT WINAPI
LZStart(void)
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
;
160 /***********************************************************************
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
171 HFILE WINAPI
LZInit( HFILE hfSrc
)
174 struct lzfileheader head
;
179 TRACE("(%d)\n",hfSrc
);
180 ret
=read_header(hfSrc
,&head
);
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
));
192 lzs
->lastchar
= head
.lastchar
;
193 lzs
->reallength
= head
.reallength
;
195 lzs
->get
= HeapAlloc( GetProcessHeap(), 0, GETLEN
);
199 if(lzs
->get
== NULL
) {
200 HeapFree(GetProcessHeap(), 0, lzs
);
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;
213 /***********************************************************************
214 * LZDone (LZEXPAND.9) (LZ32.8)
216 void WINAPI
LZDone(void)
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
;
247 INT fnislowercased
,ret
,len
;
251 fd
=OpenFile(in
,&ofs
,OF_READ
);
253 return (INT
)(INT16
)LZERROR_BADINHANDLE
;
255 ret
=read_header(fd
,&head
);
257 /* not a LZ compressed file, so the expanded name is the same
258 * as the input name */
264 /* look for directory prefix and skip it. */
266 while (NULL
!=(t
=strpbrk(s
,"/\\:")))
269 /* now mangle the basename */
271 /* FIXME: hmm. shouldn't happen? */
272 WARN("Specified a directory or what? (%s)\n",in
);
276 /* see if we should use lowercase or uppercase on the last char */
284 fnislowercased
=islower(*t
);
287 if (isalpha(head
.lastchar
)) {
289 head
.lastchar
=tolower(head
.lastchar
);
291 head
.lastchar
=toupper(head
.lastchar
);
294 /* now look where to replace the last character */
295 if (NULL
!=(t
=strchr(s
,'.'))) {
301 t
[len
]=head
.lastchar
;
303 } /* else no modification necessary */
309 /***********************************************************************
310 * GetExpandedNameW (LZ32.11)
312 INT WINAPI
GetExpandedNameW( LPCWSTR in
, LPWSTR out
)
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
);
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 /***********************************************************************
340 INT WINAPI
LZRead( HFILE fd
, LPVOID vbuf
, UINT toread
)
347 TRACE("(%d,%p,%d)\n",fd
,buf
,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; \
360 if (!(lzs->bytetype&0x100)) { \
362 return toread-howmuch; \
363 lzs->bytetype = b|0xFF00; \
365 if (lzs->bytetype & 1) { \
367 return toread-howmuch; \
371 if (1!=GET(lzs,b1)) \
372 return toread-howmuch; \
373 if (1!=GET(lzs,b2)) \
374 return toread-howmuch; \
378 * where CAB is the stringoffset in the table\
379 * and D+3 is the len of the string \
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;\
389 /* store b in table */ \
390 lzs->table[lzs->curtabent++]= b; \
391 lzs->curtabent &= 0xFFF; \
394 /* if someone has seeked, we have to bring the decompressor
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
);
409 memset(lzs
->table
,' ',0x1000);
410 lzs
->curtabent
= 0xFF0;
412 while (lzs
->realcurrent
<lzs
->realwanted
) {
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 /***********************************************************************
441 LONG WINAPI
LZSeek( HFILE fd
, LONG off
, INT type
)
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
;
451 case 1: /* SEEK_CUR */
454 case 2: /* SEEK_END */
455 newwanted
= lzs
->reallength
-off
;
457 default:/* SEEK_SET */
461 if (newwanted
>lzs
->reallength
)
462 return LZERROR_BADVALUE
;
464 return LZERROR_BADVALUE
;
465 lzs
->realwanted
= 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 */
481 if ((INT16
)src
<= 0) return 0;
482 if (IS_LZ_HANDLE(src
))
484 LONG ret
= LZCopy( src
, DosFileHandleToWin32Handle(dest
) );
488 /* it was not a compressed file */
489 return LZCopy( DosFileHandleToWin32Handle(src
), DosFileHandleToWin32Handle(dest
) );
493 /***********************************************************************
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
;
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
);
514 TRACE("(%d,%d)\n",src
,dest
);
515 if (!IS_LZ_HANDLE(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
))
525 xread
=(_readfun
)LZRead
;
528 ret
=xread(src
,buf
,BUFLEN
);
537 wret
= _lwrite(dest
,buf
,ret
);
539 return LZERROR_WRITE
;
547 /* reverses GetExpandedPathname */
548 static LPSTR
LZEXPAND_MangleName( LPCSTR fn
)
551 char *mfn
= (char *)HeapAlloc( GetProcessHeap(), 0,
552 strlen(fn
) + 3 ); /* "._" and \0 */
553 if(mfn
== NULL
) return NULL
;
555 if (!(p
= strrchr( mfn
, '\\' ))) p
= mfn
;
556 if ((p
= strchr( p
, '.' )))
559 if (strlen(p
) < 3) strcat( p
, "_" ); /* append '_' */
560 else p
[strlen(p
)-1] = '_'; /* replace last character */
562 else strcat( mfn
, "._" ); /* append "._" */
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
)
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
);
595 LPSTR mfn
= LZEXPAND_MangleName(fn
);
596 fd
= OpenFile(mfn
,ofs
,mode
);
597 HeapFree( GetProcessHeap(), 0, mfn
);
599 if ((mode
&~0x70)!=OF_READ
)
604 if ((INT
)cfd
<= 0) return fd
;
609 /***********************************************************************
610 * LZOpenFileW (LZ32.10)
612 HFILE WINAPI
LZOpenFileW( LPCWSTR fn
, LPOFSTRUCT ofs
, UINT mode
)
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
);
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 /***********************************************************************
637 void WINAPI
LZClose( HFILE fd
)
642 if (!(lzs
= GET_LZ_STATE(fd
))) _lclose(fd
);
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
);