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

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

Add: LZ32 support fixed

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