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

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

.

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