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

Last change on this file since 474 was 474, checked in by phaller, 26 years ago

Fix: Applied FS wrappers

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