Store monitor size and depth in the generic structure.
[wine/multimedia.git] / dlls / lzexpand / lzexpand_main.c
blob06fb1eacda09622078f0fd8d6c9179d8a9f5a3c2
1 /*
2 * LZ Decompression functions
4 * Copyright 1996 Marcus Meissner
5 */
6 /*
7 * FIXME: return values might be wrong
8 */
10 #include <string.h>
11 #include <ctype.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #include "windef.h"
15 #include "winbase.h"
16 #include "wine/winbase16.h"
17 #include "wine/winestring.h"
18 #include "file.h"
19 #include "heap.h"
20 #include "lzexpand.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(file);
26 /* The readahead length of the decompressor. Reading single bytes
27 * using _lread() would be SLOW.
29 #define GETLEN 2048
31 /* Format of first 14 byte of LZ compressed file */
32 struct lzfileheader {
33 BYTE magic[8];
34 BYTE compressiontype;
35 CHAR lastchar;
36 DWORD reallength;
38 static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
40 struct lzstate {
41 HFILE realfd; /* the real filedescriptor */
42 CHAR lastchar; /* the last char of the filename */
44 DWORD reallength; /* the decompressed length of the file */
45 DWORD realcurrent; /* the position the decompressor currently is */
46 DWORD realwanted; /* the position the user wants to read from */
48 BYTE table[0x1000]; /* the rotating LZ table */
49 UINT curtabent; /* CURrent TABle ENTry */
51 BYTE stringlen; /* length and position of current string */
52 DWORD stringpos; /* from stringtable */
55 WORD bytetype; /* bitmask within blocks */
57 BYTE *get; /* GETLEN bytes */
58 DWORD getcur; /* current read */
59 DWORD getlen; /* length last got */
62 #define MAX_LZSTATES 16
63 static struct lzstate *lzstates[MAX_LZSTATES];
65 #define IS_LZ_HANDLE(h) (((h) >= 0x400) && ((h) < 0x400+MAX_LZSTATES))
66 #define GET_LZ_STATE(h) (IS_LZ_HANDLE(h) ? lzstates[(h)-0x400] : NULL)
68 /* reads one compressed byte, including buffering */
69 #define GET(lzs,b) _lzget(lzs,&b)
70 #define GET_FLUSH(lzs) lzs->getcur=lzs->getlen;
72 static int
73 _lzget(struct lzstate *lzs,BYTE *b) {
74 if (lzs->getcur<lzs->getlen) {
75 *b = lzs->get[lzs->getcur++];
76 return 1;
77 } else {
78 int ret = _lread(lzs->realfd,lzs->get,GETLEN);
79 if (ret==HFILE_ERROR)
80 return HFILE_ERROR;
81 if (ret==0)
82 return 0;
83 lzs->getlen = ret;
84 lzs->getcur = 1;
85 *b = *(lzs->get);
86 return 1;
89 /* internal function, reads lzheader
90 * returns BADINHANDLE for non filedescriptors
91 * return 0 for file not compressed using LZ
92 * return UNKNOWNALG for unknown algorithm
93 * returns lzfileheader in *head
95 static INT read_header(HFILE fd,struct lzfileheader *head)
97 BYTE buf[14];
99 if (_llseek(fd,0,SEEK_SET)==-1)
100 return LZERROR_BADINHANDLE;
102 /* We can't directly read the lzfileheader struct due to
103 * structure element alignment
105 if (_lread(fd,buf,14)<14)
106 return 0;
107 memcpy(head->magic,buf,8);
108 memcpy(&(head->compressiontype),buf+8,1);
109 memcpy(&(head->lastchar),buf+9,1);
111 /* FIXME: consider endianess on non-intel architectures */
112 memcpy(&(head->reallength),buf+10,4);
114 if (memcmp(head->magic,LZMagic,8))
115 return 0;
116 if (head->compressiontype!='A')
117 return LZERROR_UNKNOWNALG;
118 return 1;
121 /***********************************************************************
122 * LZStart16 (LZEXPAND.7)
124 INT16 WINAPI LZStart16(void)
126 TRACE("(void)\n");
127 return 1;
131 /***********************************************************************
132 * LZStart32 (LZ32.6)
134 INT WINAPI LZStart(void)
136 TRACE("(void)\n");
137 return 1;
141 /***********************************************************************
142 * LZInit16 (LZEXPAND.3)
144 HFILE16 WINAPI LZInit16( HFILE16 hfSrc )
146 HFILE ret = LZInit( FILE_GetHandle(hfSrc) );
147 if (IS_LZ_HANDLE(ret)) return ret;
148 if ((INT)ret <= 0) return ret;
149 return hfSrc;
153 /***********************************************************************
154 * LZInit32 (LZ32.2)
156 * initializes internal decompression buffers, returns lzfiledescriptor.
157 * (return value the same as hfSrc, if hfSrc is not compressed)
158 * on failure, returns error code <0
159 * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
161 * since _llseek uses the same types as libc.lseek, we just use the macros of
162 * libc
164 HFILE WINAPI LZInit( HFILE hfSrc )
167 struct lzfileheader head;
168 struct lzstate *lzs;
169 DWORD ret;
170 int i;
172 TRACE("(%d)\n",hfSrc);
173 ret=read_header(hfSrc,&head);
174 if (ret<=0) {
175 _llseek(hfSrc,0,SEEK_SET);
176 return ret?ret:hfSrc;
178 for (i = 0; i < MAX_LZSTATES; i++) if (!lzstates[i]) break;
179 if (i == MAX_LZSTATES) return LZERROR_GLOBALLOC;
180 lzstates[i] = lzs = HeapAlloc( GetProcessHeap(), 0, sizeof(struct lzstate) );
182 memset(lzs,'\0',sizeof(*lzs));
183 lzs->realfd = hfSrc;
184 lzs->lastchar = head.lastchar;
185 lzs->reallength = head.reallength;
187 lzs->get = HEAP_xalloc( GetProcessHeap(), 0, GETLEN );
188 lzs->getlen = 0;
189 lzs->getcur = 0;
191 /* Yes, preinitialize with spaces */
192 memset(lzs->table,' ',0x1000);
193 /* Yes, start 16 byte from the END of the table */
194 lzs->curtabent = 0xff0;
195 return 0x400 + i;
199 /***********************************************************************
200 * LZDone (LZEXPAND.9) (LZ32.8)
202 void WINAPI LZDone(void)
204 TRACE("(void)\n");
208 /***********************************************************************
209 * GetExpandedName16 (LZEXPAND.10)
211 INT16 WINAPI GetExpandedName16( LPCSTR in, LPSTR out )
213 return (INT16)GetExpandedNameA( in, out );
217 /***********************************************************************
218 * GetExpandedName32A (LZ32.9)
220 * gets the full filename of the compressed file 'in' by opening it
221 * and reading the header
223 * "file." is being translated to "file"
224 * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
225 * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
228 INT WINAPI GetExpandedNameA( LPCSTR in, LPSTR out )
230 struct lzfileheader head;
231 HFILE fd;
232 OFSTRUCT ofs;
233 INT fnislowercased,ret,len;
234 LPSTR s,t;
236 TRACE("(%s)\n",in);
237 fd=OpenFile(in,&ofs,OF_READ);
238 if (fd==HFILE_ERROR)
239 return (INT)(INT16)LZERROR_BADINHANDLE;
240 strcpy(out,in);
241 ret=read_header(fd,&head);
242 if (ret<=0) {
243 /* not a LZ compressed file, so the expanded name is the same
244 * as the input name */
245 _lclose(fd);
246 return 1;
250 /* look for directory prefix and skip it. */
251 s=out;
252 while (NULL!=(t=strpbrk(s,"/\\:")))
253 s=t+1;
255 /* now mangle the basename */
256 if (!*s) {
257 /* FIXME: hmm. shouldn't happen? */
258 WARN("Specified a directory or what? (%s)\n",in);
259 _lclose(fd);
260 return 1;
262 /* see if we should use lowercase or uppercase on the last char */
263 fnislowercased=1;
264 t=s+strlen(s)-1;
265 while (t>=out) {
266 if (!isalpha(*t)) {
267 t--;
268 continue;
270 fnislowercased=islower(*t);
271 break;
273 if (isalpha(head.lastchar)) {
274 if (fnislowercased)
275 head.lastchar=tolower(head.lastchar);
276 else
277 head.lastchar=toupper(head.lastchar);
280 /* now look where to replace the last character */
281 if (NULL!=(t=strchr(s,'.'))) {
282 if (t[1]=='\0') {
283 t[0]='\0';
284 } else {
285 len=strlen(t)-1;
286 if (t[len]=='_')
287 t[len]=head.lastchar;
289 } /* else no modification necessary */
290 _lclose(fd);
291 return 1;
295 /***********************************************************************
296 * GetExpandedName32W (LZ32.11)
298 INT WINAPI GetExpandedNameW( LPCWSTR in, LPWSTR out )
300 char *xin,*xout;
301 INT ret;
303 xout = HeapAlloc( GetProcessHeap(), 0, lstrlenW(in)+3 );
304 xin = HEAP_strdupWtoA( GetProcessHeap(), 0, in );
305 ret = GetExpandedName16(xin,xout);
306 if (ret>0) lstrcpyAtoW(out,xout);
307 HeapFree( GetProcessHeap(), 0, xin );
308 HeapFree( GetProcessHeap(), 0, xout );
309 return ret;
313 /***********************************************************************
314 * LZRead16 (LZEXPAND.5)
316 INT16 WINAPI LZRead16( HFILE16 fd, LPVOID buf, UINT16 toread )
318 if (IS_LZ_HANDLE(fd)) return LZRead( fd, buf, toread );
319 return _lread16( fd, buf, toread );
323 /***********************************************************************
324 * LZRead32 (LZ32.4)
326 INT WINAPI LZRead( HFILE fd, LPVOID vbuf, UINT toread )
328 int howmuch;
329 BYTE b,*buf;
330 struct lzstate *lzs;
332 buf=(LPBYTE)vbuf;
333 TRACE("(%d,%p,%d)\n",fd,buf,toread);
334 howmuch=toread;
335 if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread);
337 /* The decompressor itself is in a define, cause we need it twice
338 * in this function. (the decompressed byte will be in b)
340 #define DECOMPRESS_ONE_BYTE \
341 if (lzs->stringlen) { \
342 b = lzs->table[lzs->stringpos]; \
343 lzs->stringpos = (lzs->stringpos+1)&0xFFF; \
344 lzs->stringlen--; \
345 } else { \
346 if (!(lzs->bytetype&0x100)) { \
347 if (1!=GET(lzs,b)) \
348 return toread-howmuch; \
349 lzs->bytetype = b|0xFF00; \
351 if (lzs->bytetype & 1) { \
352 if (1!=GET(lzs,b)) \
353 return toread-howmuch; \
354 } else { \
355 BYTE b1,b2; \
357 if (1!=GET(lzs,b1)) \
358 return toread-howmuch; \
359 if (1!=GET(lzs,b2)) \
360 return toread-howmuch; \
361 /* Format: \
362 * b1 b2 \
363 * AB CD \
364 * where CAB is the stringoffset in the table\
365 * and D+3 is the len of the string \
366 */ \
367 lzs->stringpos = b1|((b2&0xf0)<<4); \
368 lzs->stringlen = (b2&0xf)+2; \
369 /* 3, but we use a byte already below ... */\
370 b = lzs->table[lzs->stringpos];\
371 lzs->stringpos = (lzs->stringpos+1)&0xFFF;\
373 lzs->bytetype>>=1; \
375 /* store b in table */ \
376 lzs->table[lzs->curtabent++]= b; \
377 lzs->curtabent &= 0xFFF; \
378 lzs->realcurrent++;
380 /* if someone has seeked, we have to bring the decompressor
381 * to that position
383 if (lzs->realcurrent!=lzs->realwanted) {
384 /* if the wanted position is before the current position
385 * I see no easy way to unroll ... We have to restart at
386 * the beginning. *sigh*
388 if (lzs->realcurrent>lzs->realwanted) {
389 /* flush decompressor state */
390 _llseek(lzs->realfd,14,SEEK_SET);
391 GET_FLUSH(lzs);
392 lzs->realcurrent= 0;
393 lzs->bytetype = 0;
394 lzs->stringlen = 0;
395 memset(lzs->table,' ',0x1000);
396 lzs->curtabent = 0xFF0;
398 while (lzs->realcurrent<lzs->realwanted) {
399 DECOMPRESS_ONE_BYTE;
403 while (howmuch) {
404 DECOMPRESS_ONE_BYTE;
405 lzs->realwanted++;
406 *buf++ = b;
407 howmuch--;
409 return toread;
410 #undef DECOMPRESS_ONE_BYTE
414 /***********************************************************************
415 * LZSeek16 (LZEXPAND.4)
417 LONG WINAPI LZSeek16( HFILE16 fd, LONG off, INT16 type )
419 if (IS_LZ_HANDLE(fd)) return LZSeek( fd, off, type );
420 return _llseek16( fd, off, type );
424 /***********************************************************************
425 * LZSeek32 (LZ32.3)
427 LONG WINAPI LZSeek( HFILE fd, LONG off, INT type )
429 struct lzstate *lzs;
430 LONG newwanted;
432 TRACE("(%d,%ld,%d)\n",fd,off,type);
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;
447 if (newwanted>lzs->reallength)
448 return LZERROR_BADVALUE;
449 if (newwanted<0)
450 return LZERROR_BADVALUE;
451 lzs->realwanted = newwanted;
452 return newwanted;
456 /***********************************************************************
457 * LZCopy16 (LZEXPAND.1)
460 LONG WINAPI LZCopy16( HFILE16 src, HFILE16 dest )
462 /* already a LZ handle? */
463 if (IS_LZ_HANDLE(src)) return LZCopy( src, FILE_GetHandle(dest) );
465 /* no, try to open one */
466 src = LZInit16(src);
467 if ((INT16)src <= 0) return 0;
468 if (IS_LZ_HANDLE(src))
470 LONG ret = LZCopy( src, FILE_GetHandle(dest) );
471 LZClose( src );
472 return ret;
474 /* it was not a compressed file */
475 return LZCopy( FILE_GetHandle(src), FILE_GetHandle(dest) );
479 /***********************************************************************
480 * LZCopy32 (LZ32.0)
482 * Copies everything from src to dest
483 * if src is a LZ compressed file, it will be uncompressed.
484 * will return the number of bytes written to dest or errors.
486 LONG WINAPI LZCopy( HFILE src, HFILE dest )
488 int usedlzinit=0,ret,wret;
489 LONG len;
490 HFILE oldsrc = src;
491 #define BUFLEN 1000
492 BYTE buf[BUFLEN];
493 /* we need that weird typedef, for i can't seem to get function pointer
494 * casts right. (Or they probably just do not like WINAPI in general)
496 typedef UINT WINAPI (*_readfun)(HFILE,LPVOID,UINT);
498 _readfun xread;
500 TRACE("(%d,%d)\n",src,dest);
501 if (!IS_LZ_HANDLE(src)) {
502 src = LZInit(src);
503 if ((INT)src <= 0) return 0;
504 if (src != oldsrc) usedlzinit=1;
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;
522 len += ret;
523 wret = _lwrite(dest,buf,ret);
524 if (wret!=ret)
525 return LZERROR_WRITE;
527 if (usedlzinit)
528 LZClose(src);
529 return len;
530 #undef BUFLEN
533 /* reverses GetExpandedPathname */
534 static LPSTR LZEXPAND_MangleName( LPCSTR fn )
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, '.' )))
543 p++;
544 if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */
545 else p[strlen(p)-1] = '_'; /* replace last character */
547 else strcat( mfn, "._" ); /* append "._" */
548 return mfn;
552 /***********************************************************************
553 * LZOpenFile16 (LZEXPAND.2)
555 HFILE16 WINAPI LZOpenFile16( LPCSTR fn, LPOFSTRUCT ofs, UINT16 mode )
557 HFILE hfret = LZOpenFileA( fn, ofs, mode );
558 /* return errors and LZ handles unmodified */
559 if ((INT)hfret <= 0) return hfret;
560 if (IS_LZ_HANDLE(hfret)) return hfret;
561 /* but allocate a dos handle for 'normal' files */
562 return FILE_AllocDosHandle(hfret);
566 /***********************************************************************
567 * LZOpenFile32A (LZ32.1)
569 * Opens a file. If not compressed, open it as a normal file.
571 HFILE WINAPI LZOpenFileA( LPCSTR fn, LPOFSTRUCT ofs, UINT mode )
573 HFILE fd,cfd;
575 TRACE("(%s,%p,%d)\n",fn,ofs,mode);
576 /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
577 fd=OpenFile(fn,ofs,mode);
578 if (fd==HFILE_ERROR)
580 LPSTR mfn = LZEXPAND_MangleName(fn);
581 fd = OpenFile(mfn,ofs,mode);
582 HeapFree( GetProcessHeap(), 0, mfn );
584 if ((mode&~0x70)!=OF_READ)
585 return fd;
586 if (fd==HFILE_ERROR)
587 return HFILE_ERROR;
588 cfd=LZInit(fd);
589 if ((INT)cfd <= 0) return fd;
590 return cfd;
594 /***********************************************************************
595 * LZOpenFile32W (LZ32.10)
597 HFILE WINAPI LZOpenFileW( LPCWSTR fn, LPOFSTRUCT ofs, UINT mode )
599 LPSTR xfn;
600 LPWSTR yfn;
601 HFILE ret;
603 xfn = HEAP_strdupWtoA( GetProcessHeap(), 0, fn);
604 ret = LZOpenFile16(xfn,ofs,mode);
605 HeapFree( GetProcessHeap(), 0, xfn );
606 if (ret!=HFILE_ERROR) {
607 /* ofs->szPathName is an array with the OFSTRUCT */
608 yfn = HEAP_strdupAtoW( GetProcessHeap(), 0, ofs->szPathName );
609 memcpy(ofs->szPathName,yfn,lstrlenW(yfn)*2+2);
610 HeapFree( GetProcessHeap(), 0, yfn );
612 return ret;
616 /***********************************************************************
617 * LZClose16 (LZEXPAND.6)
619 void WINAPI LZClose16( HFILE16 fd )
621 if (IS_LZ_HANDLE(fd)) LZClose( fd );
622 else _lclose16( fd );
626 /***********************************************************************
627 * LZClose32 (LZ32.5)
629 void WINAPI LZClose( HFILE fd )
631 struct lzstate *lzs;
633 TRACE("(%d)\n",fd);
634 if (!(lzs = GET_LZ_STATE(fd))) _lclose(fd);
635 else
637 if (lzs->get) HeapFree( GetProcessHeap(), 0, lzs->get );
638 CloseHandle(lzs->realfd);
639 lzstates[fd - 0x400] = NULL;
640 HeapFree( GetProcessHeap(), 0, lzs );
644 /***********************************************************************
645 * CopyLZFile16 (LZEXPAND.8)
647 LONG WINAPI CopyLZFile16( HFILE16 src, HFILE16 dest )
649 TRACE("(%d,%d)\n",src,dest);
650 return LZCopy16(src,dest);
654 /***********************************************************************
655 * CopyLZFile32 (LZ32.7)
657 * Copy src to dest (including uncompressing src).
658 * NOTE: Yes. This is exactly the same function as LZCopy.
660 LONG WINAPI CopyLZFile( HFILE src, HFILE dest )
662 TRACE("(%d,%d)\n",src,dest);
663 return LZCopy(src,dest);