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

Last change on this file since 530 was 530, checked in by sandervl, 26 years ago

Compilation fixes

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