source: trunk/src/lz32/lz32.cpp@ 10367

Last change on this file since 10367 was 3695, checked in by phaller, 25 years ago

Updated to current WINE Level 20000612

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