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

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

Removed prototype of lstrcpyAtoW

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