Initial commit.

This commit is contained in:
Ronald Kinard 2015-02-23 00:12:26 -06:00
commit e4280d7bc7
13 changed files with 1395 additions and 0 deletions

42
README.md Normal file
View file

@ -0,0 +1,42 @@
# wadzip utility
SRB2's own zipped WAD format compression tool.
## License
Copyright (C) Sonic Team Junior, 2015
This code is made available under the terms of the GNU General Public License, version 2. You can see that license [here](http://www.gnu.org/licenses/gpl-2.0.html).
## Instructions
Compressed wad format:
Just like a normal wad, except:
1. The header id at the very beginning says "ZWAD"
instead of PWAD or IWAD.
2. The first four bytes of each lump are (little
endian) the uncompressed size. If this is zero,
the lump is not compressed, and you can subtract
four from the size given in the wadfile directory.
3. Unless those first four bytes give a value of
0, the lump is compressed with liblzf after that.
To compile this program, just compiling all its
source files should work, that is:
gcc *.c -o wadzip
[On a big-endian CPU, add "-DWORDS_BIGENDIAN" to
that command line.]
Visual C++ should also work, as well as gcc.
Compress a wad:
wadzip c original.wad compressed.wad
Decompress a wad (restoring the original):
wadzip d compressed.wad original.wad

121
err.c Normal file
View file

@ -0,0 +1,121 @@
#if defined (__WIN32) || defined (WIN32)
#define NEED_ERR
#define LACK_PROGNAME
#define NEED_ENDIAN_STUBS
#define LACK_SYSTYPES
#endif
#ifdef NEED_ERR
/*
* Implementation of the err/errx/verr/verrx/warn/warnx/vwarn/vwarnx
* functions from BSD.
*
* This file is public-domain; anyone may deal in it without restriction.
*
* Written by Graue <graue@oceanbase.org> on January 16, 2006.
*/
/*
err/warn family of functions cheat sheet:
Print:
last component of program name
[ if fmt is non-NULL
": "
the formatted error message
]
[ if function name does not end in x
": "
strerror(errno)
]
newline
Then if function name has "err" in it, quit with exit code `eval'.
BSD's -x versions actually print ": " at the end if passed NULL, so I
duplicate that behavior. Passing these functions NULL is kind of useless
though.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#ifdef LACK_PROGNAME
char *__progname = "wadzip";
#else
extern char *__progname;
#endif
#define progname __progname
void vwarn(const char *fmt, va_list args)
{
fputs(progname, stderr);
if (fmt != NULL)
{
fputs(": ", stderr);
vfprintf(stderr, fmt, args);
}
fputs(": ", stderr);
fputs(strerror(errno), stderr);
putc('\n', stderr);
}
void vwarnx(const char *fmt, va_list args)
{
fputs(progname, stderr);
fputs(": ", stderr);
if (fmt != NULL)
vfprintf(stderr, fmt, args);
putc('\n', stderr);
}
void verr(int eval, const char *fmt, va_list args)
{
vwarn(fmt, args);
exit(eval);
}
void verrx(int eval, const char *fmt, va_list args)
{
vwarnx(fmt, args);
exit(eval);
}
void warn(const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
vwarn(fmt, argptr);
va_end(argptr);
}
void warnx(const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
vwarnx(fmt, argptr);
va_end(argptr);
}
void err(int eval, const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
verr(eval, fmt, argptr);
/* NOTREACHED, so don't worry about va_end() */
}
void errx(int eval, const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
verrx(eval, fmt, argptr);
/* NOTREACHED, so don't worry about va_end() */
}
#endif

26
err.h Normal file
View file

@ -0,0 +1,26 @@
#if defined (__WIN32) || defined (WIN32)
#define NEED_ERR
#endif
#ifndef NEED_ERR
#include <err.h> /* system version of this file */
#else
#ifndef _ERR_H
#define _ERR_H
#include <stdarg.h>
void err (int eval, const char *fmt, ...);
void errx (int eval, const char *fmt, ...);
void verr (int eval, const char *fmt, va_list args);
void verrx (int eval, const char *fmt, va_list args);
void warn ( const char *fmt, ...);
void warnx( const char *fmt, ...);
void vwarn ( const char *fmt, va_list args);
void vwarnx( const char *fmt, va_list args);
#endif
#endif

101
lzf.h Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2000-2005 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License version 2 (the "GPL"), in which case the
* provisions of the GPL are applicable instead of the above. If you wish to
* allow the use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* BSD license, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL. If
* you do not delete the provisions above, a recipient may use your version
* of this file under either the BSD or the GPL.
*/
#ifndef LZF_H
#define LZF_H
/***********************************************************************
**
** lzf -- an extremely fast/free compression/decompression-method
** http://liblzf.plan9.de/
**
** This algorithm is believed to be patent-free.
**
***********************************************************************/
#define LZF_VERSION 0x0105 /* 1.5 */
/*
* Compress in_len bytes stored at the memory block starting at
* in_data and write the result to out_data, up to a maximum length
* of out_len bytes.
*
* If the output buffer is not large enough or any error occurs
* return 0, otherwise return the number of bytes used (which might
* be considerably larger than in_len, so it makes sense to always
* use out_len == in_len - 1), to ensure _some_ compression, and store
* the data uncompressed otherwise.
*
* lzf_compress might use different algorithms on different systems and
* even diferent runs, thus might result in different compressed strings
* depending on the phase of the moon or similar factors. However, all
* these strings are architecture-independent and will result in the
* original data when decompressed using lzf_decompress.
*
* The buffers must not be overlapping.
*
* If the option LZF_STATE_ARG is enabled, an extra argument must be
* supplied which is not reflected in this header file. Refer to lzfP.h
* and lzf_c.c.
*
*/
unsigned int
lzf_compress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len);
/*
* Decompress data compressed with some version of the lzf_compress
* function and stored at location in_data and length in_len. The result
* will be stored at out_data up to a maximum of out_len characters.
*
* If the output buffer is not large enough to hold the decompressed
* data, a 0 is returned and errno is set to E2BIG. Otherwise the number
* of decompressed bytes (i.e. the original length of the data) is
* returned.
*
* If an error in the compressed data is detected, a zero is returned and
* errno is set to EINVAL.
*
* This function is very fast, about as fast as a copying loop.
*/
unsigned int
lzf_decompress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len);
#endif

169
lzfP.h Normal file
View file

@ -0,0 +1,169 @@
/*
* Copyright (c) 2000-2005 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License version 2 (the "GPL"), in which case the
* provisions of the GPL are applicable instead of the above. If you wish to
* allow the use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* BSD license, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL. If
* you do not delete the provisions above, a recipient may use your version
* of this file under either the BSD or the GPL.
*/
#ifndef LZFP_h
#define LZFP_h
#define STANDALONE 1 /* at the moment, this is ok. */
#ifndef STANDALONE
# include "lzf.h"
#endif
/*
* Size of hashtable is (1 << HLOG) * sizeof (char *)
* decompression is independent of the hash table size
* the difference between 15 and 14 is very small
* for small blocks (and 14 is usually a bit faster).
* For a low-memory/faster configuration, use HLOG == 13;
* For best compression, use 15 or 16 (or more).
*/
#ifndef HLOG
# define HLOG 15
#endif
/*
* Sacrifice very little compression quality in favour of compression speed.
* This gives almost the same compression as the default code, and is
* (very roughly) 15% faster. This is the preferable mode of operation.
*/
#ifndef VERY_FAST
# define VERY_FAST 1
#endif
/*
* Sacrifice some more compression quality in favour of compression speed.
* (roughly 1-2% worse compression for large blocks and
* 9-10% for small, redundant, blocks and >>20% better speed in both cases)
* In short: when in need for speed, enable this for binary data,
* possibly disable this for text data.
*/
#ifndef ULTRA_FAST
# define ULTRA_FAST 0
#endif
/*
* Unconditionally aligning does not cost very much, so do it if unsure
*/
#ifndef STRICT_ALIGN
# define STRICT_ALIGN !(defined(__i386) || defined (__amd64))
#endif
/*
* Use string functions to copy memory.
* this is usually a loss, even with glibc's optimized memcpy
*/
#ifndef USE_MEMCPY
# define USE_MEMCPY 0
#endif
/*
* You may choose to pre-set the hash table (might be faster on some
* modern cpus and large (>>64k) blocks)
*/
#ifndef INIT_HTAB
# define INIT_HTAB 0
#endif
/*
* Avoid assigning values to errno variable? for some embedding purposes
* (linux kernel for example), this is neccessary. NOTE: this breaks
* the documentation in lzf.h.
*/
#ifndef AVOID_ERRNO
# define AVOID_ERRNO 0
#endif
/*
* Wether to pass the LZF_STATE variable as argument, or allocate it
* on the stack. For small-stack environments, define this to 1.
* NOTE: this breaks the prototype in lzf.h.
*/
#ifndef LZF_STATE_ARG
# define LZF_STATE_ARG 0
#endif
/*
* Wether to add extra checks for input validity in lzf_decompress
* and return EINVAL if the input stream has been corrupted. This
* only shields against overflowing the input buffer and will not
* detect most corrupted streams.
* This check is not normally noticable on modern hardware
* (<1% slowdown), but might slow down older cpus considerably.
*/
#ifndef CHECK_INPUT
# define CHECK_INPUT 1
#endif
/*****************************************************************************/
/* nothing should be changed below */
typedef unsigned char u8;
typedef const u8 *LZF_STATE[1 << (HLOG)];
#if !STRICT_ALIGN
/* for unaligned accesses we need a 16 bit datatype. */
# include <limits.h>
# if USHRT_MAX == 65535
typedef unsigned short u16;
# elif UINT_MAX == 65535
typedef unsigned int u16;
# else
# undef STRICT_ALIGN
# define STRICT_ALIGN 1
# endif
#endif
#if ULTRA_FAST
# if defined(VERY_FAST)
# undef VERY_FAST
# endif
#endif
#if USE_MEMCPY || INIT_HTAB
# ifdef __cplusplus
# include <cstring>
# else
# include <string.h>
# endif
#endif
#endif

242
lzf_c.c Normal file
View file

@ -0,0 +1,242 @@
/*
* Copyright (c) 2000-2005 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License version 2 (the "GPL"), in which case the
* provisions of the GPL are applicable instead of the above. If you wish to
* allow the use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* BSD license, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL. If
* you do not delete the provisions above, a recipient may use your version
* of this file under either the BSD or the GPL.
*/
#include "lzfP.h"
#define HSIZE (1 << (HLOG))
/*
* don't play with this unless you benchmark!
* decompression is not dependent on the hash function
* the hashing function might seem strange, just believe me
* it works ;)
*/
#ifndef FRST
# define FRST(p) (((p[0]) << 8) | p[1])
# define NEXT(v,p) (((v) << 8) | p[2])
# define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))
#endif
/*
* IDX works because it is very similar to a multiplicative hash, e.g.
* ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1))
* the latter is also quite fast on newer CPUs, and sligthly better
*
* the next one is also quite good, albeit slow ;)
* (int)(cos(h & 0xffffff) * 1e6)
*/
#if 0
/* original lzv-like hash function, much worse and thus slower */
# define FRST(p) (p[0] << 5) ^ p[1]
# define NEXT(v,p) ((v) << 5) ^ p[2]
# define IDX(h) ((h) & (HSIZE - 1))
#endif
#define MAX_LIT (1 << 5)
#define MAX_OFF (1 << 13)
#define MAX_REF ((1 << 8) + (1 << 3))
/*
* compressed format
*
* 000LLLLL <L+1> ; literal
* LLLooooo oooooooo ; backref L
* 111ooooo LLLLLLLL oooooooo ; backref L+7
*
*/
unsigned int
lzf_compress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len
#if LZF_STATE_ARG
, LZF_STATE *htab
#endif
)
{
#if !LZF_STATE_ARG
LZF_STATE htab;
#endif
const u8 **hslot;
const u8 *ip = (const u8 *)in_data;
u8 *op = (u8 *)out_data;
const u8 *in_end = ip + in_len;
u8 *out_end = op + out_len;
const u8 *ref;
unsigned int hval = FRST (ip);
unsigned long off;
int lit = 0;
#if INIT_HTAB
# if USE_MEMCPY
memset (htab, 0, sizeof (htab));
# else
for (hslot = htab; hslot < htab + HSIZE; hslot++)
*hslot++ = ip;
# endif
#endif
for (;;)
{
if (ip < in_end - 2)
{
hval = NEXT (hval, ip);
hslot = htab + IDX (hval);
ref = *hslot; *hslot = ip;
if (1
#if INIT_HTAB && !USE_MEMCPY
&& ref < ip /* the next test will actually take care of this, but this is faster */
#endif
&& (off = ip - ref - 1) < MAX_OFF
&& ip + 4 < in_end
&& ref > (u8 *)in_data
#if STRICT_ALIGN
&& ref[0] == ip[0]
&& ref[1] == ip[1]
&& ref[2] == ip[2]
#else
&& *(u16 *)ref == *(u16 *)ip
&& ref[2] == ip[2]
#endif
)
{
/* match found at *ref++ */
unsigned int len = 2;
unsigned int maxlen = in_end - ip - len;
maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;
if (op + lit + 1 + 3 >= out_end)
return 0;
do
len++;
while (len < maxlen && ref[len] == ip[len]);
if (lit)
{
*op++ = lit - 1;
lit = -lit;
do
*op++ = ip[lit];
while (++lit);
}
len -= 2;
ip++;
if (len < 7)
{
*op++ = (off >> 8) + (len << 5);
}
else
{
*op++ = (off >> 8) + ( 7 << 5);
*op++ = len - 7;
}
*op++ = off;
#if ULTRA_FAST || VERY_FAST
ip += len;
#if VERY_FAST && !ULTRA_FAST
--ip;
#endif
hval = FRST (ip);
hval = NEXT (hval, ip);
htab[IDX (hval)] = ip;
ip++;
#if VERY_FAST && !ULTRA_FAST
hval = NEXT (hval, ip);
htab[IDX (hval)] = ip;
ip++;
#endif
#else
do
{
hval = NEXT (hval, ip);
htab[IDX (hval)] = ip;
ip++;
}
while (len--);
#endif
continue;
}
}
else if (ip == in_end)
break;
/* one more literal byte we must copy */
lit++;
ip++;
if (lit == MAX_LIT)
{
if (op + 1 + MAX_LIT >= out_end)
return 0;
*op++ = MAX_LIT - 1;
#if USE_MEMCPY
memcpy (op, ip - MAX_LIT, MAX_LIT);
op += MAX_LIT;
lit = 0;
#else
lit = -lit;
do
*op++ = ip[lit];
while (++lit);
#endif
}
}
if (lit)
{
if (op + lit + 1 >= out_end)
return 0;
*op++ = lit - 1;
lit = -lit;
do
*op++ = ip[lit];
while (++lit);
}
return op - (u8 *) out_data;
}

140
lzf_d.c Normal file
View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2000-2005 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License version 2 (the "GPL"), in which case the
* provisions of the GPL are applicable instead of the above. If you wish to
* allow the use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* BSD license, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL. If
* you do not delete the provisions above, a recipient may use your version
* of this file under either the BSD or the GPL.
*/
#include "lzfP.h"
#if AVOID_ERRNO
# define SET_ERRNO(n)
#else
# include <errno.h>
# define SET_ERRNO(n) errno = (n)
#endif
unsigned int
lzf_decompress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len)
{
u8 const *ip = (const u8 *)in_data;
u8 *op = (u8 *)out_data;
u8 const *const in_end = ip + in_len;
u8 *const out_end = op + out_len;
do
{
unsigned int ctrl = *ip++;
if (ctrl < (1 << 5)) /* literal run */
{
ctrl++;
if (op + ctrl > out_end)
{
SET_ERRNO (E2BIG);
return 0;
}
#if CHECK_INPUT
if (ip + ctrl > in_end)
{
SET_ERRNO (EINVAL);
return 0;
}
#endif
#if USE_MEMCPY
memcpy (op, ip, ctrl);
op += ctrl;
ip += ctrl;
#else
do
*op++ = *ip++;
while (--ctrl);
#endif
}
else /* back reference */
{
unsigned int len = ctrl >> 5;
u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;
#if CHECK_INPUT
if (ip >= in_end)
{
SET_ERRNO (EINVAL);
return 0;
}
#endif
if (len == 7)
{
len += *ip++;
#if CHECK_INPUT
if (ip >= in_end)
{
SET_ERRNO (EINVAL);
return 0;
}
#endif
}
ref -= *ip++;
if (op + len + 2 > out_end)
{
SET_ERRNO (E2BIG);
return 0;
}
if (ref < (u8 *)out_data)
{
SET_ERRNO (EINVAL);
return 0;
}
*op++ = *ref++;
*op++ = *ref++;
do
*op++ = *ref++;
while (--len);
}
}
while (ip < in_end);
return op - (u8 *)out_data;
}

311
wadzip.c Normal file
View file

@ -0,0 +1,311 @@
/*
* Wad compressor/decompressor by Graue <graue@oceanbase.org>
* Written January 23, 2007
*
* This file is in the public domain; use it without any restrictions.
*/
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <stdint.h>
#else
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
#endif
#include <errno.h>
#include <string.h>
#include "err.h"
#include "lzf.h"
#include "xm.h"
/* Note: Define WORDS_BIGENDIAN if that is the case on the target CPU. */
#define MINCOMPRESS 1024 // Don't bother compressing lumps smaller than this.
#define SWAP16(n) ((((n)&0xff)<<8) | ((n)>>8))
#define SWAP32(n) \
( \
((((n)&0xff) <<24) \
| ((((n)>>8)&0xff)<<16) \
| ((((n)>>16)&0xff)<<8) \
| ((n)>>24) \
)
#ifdef WORDS_BIGENDIAN
#define conv16le SWAP16
#define conv32le SWAP32
#else
#define conv16le(n) (n)
#define conv32le(n) (n)
#endif
#define READFUNC(name, type, conv) \
static type name(FILE *fp) \
{ \
type val; \
if (fread(&val, sizeof val, 1, fp) < 1) \
{ \
if (ferror(fp)) \
err(1, "error reading input file"); \
errx(1, "premature end of input file"); \
} \
val = conv(val); \
return val; \
}
READFUNC(read32le, uint32_t, conv32le)
#define WRITEFUNC(name, type, conv) \
static void name(FILE *fp, type val) \
{ \
val = conv(val); \
if (fwrite(&val, sizeof (type), 1, fp) < 1) \
err(1, "error writing output file"); \
}
WRITEFUNC(write32le, uint32_t, conv32le)
#define ID_IWAD 0x44415749
#define ID_PWAD 0x44415750
#define ID_ZWAD 0x4441575a // 'ZWAD' for lzf-compressed wad
typedef struct
{
char name[8];
uint32_t len;
void *data;
} lump_t;
typedef struct
{
uint32_t id;
uint32_t numlumps;
lump_t *lumps;
} wad_t;
extern char *__progname;
static void usage(void)
{
fprintf(stderr, "usage: %s [cd] infile outfile\n", __progname);
exit(EXIT_FAILURE);
}
#define fileoffset_t long
#define myseek fseek
#define mytell ftell
static void readlumpdata(lump_t *lump, uint32_t csize, int compressed, FILE *fp)
{
uint8_t *cbuf;
uint8_t *ubuf;
uint32_t usize;
unsigned int retval;
cbuf = xm(1, csize);
if (fread(cbuf, csize, 1, fp) < 1)
err(1, "cannot read lump from input file");
if (!compressed)
{
lump->len = csize;
lump->data = cbuf;
return;
}
usize = conv32le(*(uint32_t *)cbuf);
if (usize == 0)
{
lump->len = csize - 4;
lump->data = cbuf + 4; // XXX cannot be freed later
return;
}
ubuf = xm(1, usize);
retval = lzf_decompress(cbuf + 4, csize - 4, ubuf, usize);
if (retval == 0)
{
if (errno == E2BIG)
errx(1, "decompressed data bigger than advertised");
if (errno == EINVAL)
errx(1, "invalid compressed (lzf) data");
else
err(1, "unknown error from lzf");
}
else if (retval < usize)
errx(1, "decompressed data smaller than advertised");
lump->len = usize;
lump->data = ubuf;
free(cbuf);
return;
}
static wad_t *readwad(const char *fname)
{
wad_t *wad;
FILE *fp;
int inputcompressed;
fileoffset_t dirstart;
uint32_t ix;
fp = fopen(fname, "rb");
if (fp == NULL) err(1, "%s", fname);
wad = xm(sizeof *wad, 1);
// Read wad id. If it is ZWAD, prepare to read compressed lumps.
wad->id = read32le(fp);
inputcompressed = wad->id == ID_ZWAD;
// Read number of lumps, and prepare space for that many.
wad->numlumps = read32le(fp);
wad->lumps = xm(sizeof wad->lumps[0], wad->numlumps);
// Read offset to directory.
dirstart = (fileoffset_t)read32le(fp);
for (ix = 0; ix < wad->numlumps; ix++)
{
fileoffset_t lumpofs;
uint32_t lumpsize; // The compressed size, if it is compressed.
if (myseek(fp, dirstart + (16*ix), SEEK_SET) == -1)
{
err(1, "cannot seek input file to dir entry %lu",
(unsigned long)ix);
}
lumpofs = (fileoffset_t)read32le(fp);
lumpsize = read32le(fp);
if (fread(wad->lumps[ix].name, 1, 8, fp) < 8)
err(1, "cannot read lump %lu name", (unsigned long)ix);
// Don't try to seek to zero-length lumps.
// Their offset is meaningless.
if (lumpsize == 0)
{
wad->lumps[ix].len = 0;
continue;
}
if (myseek(fp, lumpofs, SEEK_SET) == -1)
err(1, "cannot seek to lump %lu", (unsigned long)ix);
readlumpdata(&wad->lumps[ix], lumpsize, inputcompressed, fp);
}
if (fclose(fp) == EOF)
warn("error closing input file %s", fname);
return wad;
}
void writewad(const wad_t *wad, const char *fname, int compress)
{
uint32_t *lumpofs; // Each lump's offset in the file.
uint32_t *disksize; // Each lump's disk size (compressed if applicable).
uint32_t dirstart;
uint32_t ix;
FILE *fp;
lumpofs = xm(sizeof lumpofs[0], wad->numlumps);
disksize = xm(sizeof disksize[0], wad->numlumps);
fp = fopen(fname, "wb");
if (fp == NULL) err(1, "%s", fname);
// Write header.
write32le(fp, compress ? ID_ZWAD : ID_PWAD);
write32le(fp, wad->numlumps);
write32le(fp, 0); // Directory table comes later.
for (ix = 0; ix < wad->numlumps; ix++)
{
uint8_t *cbuf;
uint32_t csize;
lumpofs[ix] = (uint32_t)mytell(fp);
if (!compress)
{
cbuf = wad->lumps[ix].data;
csize = wad->lumps[ix].len;
}
else
{
unsigned int retval;
csize = wad->lumps[ix].len + 4;
cbuf = xm(1, csize);
if (wad->lumps[ix].len < MINCOMPRESS
|| (retval = lzf_compress(wad->lumps[ix].data,
wad->lumps[ix].len, cbuf + 4,
csize - 5)) == 0)
{
// Store uncompressed.
memcpy(cbuf + 4, wad->lumps[ix].data,
wad->lumps[ix].len);
*(uint32_t *)cbuf = 0;
}
else
{
// Compression succeeded.
// Store uncompressed size, and set compressed
// size properly.
*(uint32_t *)cbuf =
conv32le(wad->lumps[ix].len);
csize = retval + 4;
}
}
if (!csize)
; // inu: 0 length markers aren't to be written
else if (fwrite(cbuf, csize, 1, fp) < 1)
{
err(1, "cannot write lump %lu to %s", (unsigned long)ix,
fname);
}
if (compress)
free(cbuf);
disksize[ix] = csize;
}
dirstart = (uint32_t)mytell(fp);
if (myseek(fp, 8L, SEEK_SET) == -1)
err(1, "cannot reseek %s to write directory start", fname);
write32le(fp, dirstart);
if (myseek(fp, dirstart, SEEK_SET) == -1)
err(1, "cannot reseek %s to write actual directory", fname);
for (ix = 0; ix < wad->numlumps; ix++)
{
// Write the lump's directory entry.
write32le(fp, lumpofs[ix]);
write32le(fp, disksize[ix]);
if (fwrite(wad->lumps[ix].name, 1, 8, fp) < 8)
{
err(1, "cannot write lump %lu's name",
(unsigned long)ix);
}
}
if (fclose(fp) == EOF)
warn("error closing output file %s", fname);
}
int main(int argc, char *argv[])
{
const char *infile, *outfile;
wad_t *wad;
int compress;
if (argc != 4 || strlen(argv[1]) != 1)
usage();
/* c to compress, d to decompress */
if (argv[1][0] == 'c') compress = 1;
else if (argv[1][0] == 'd') compress = 0;
else usage();
infile = argv[2];
outfile = argv[3];
wad = readwad(infile);
writewad(wad, outfile, compress);
return 0;
}

104
wadzip.dsp Normal file
View file

@ -0,0 +1,104 @@
# Microsoft Developer Studio Project File - Name="wadzip" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=wadzip - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "wadzip.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "wadzip.mak" CFG="wadzip - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "wadzip - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "wadzip - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "wadzip - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "wadzip - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "wadzip - Win32 Release"
# Name "wadzip - Win32 Debug"
# Begin Source File
SOURCE=.\err.c
# End Source File
# Begin Source File
SOURCE=.\lzf_c.c
# End Source File
# Begin Source File
SOURCE=.\lzf_d.c
# End Source File
# Begin Source File
SOURCE=.\wadzip.c
# End Source File
# Begin Source File
SOURCE=.\xm.c
# End Source File
# End Target
# End Project

29
wadzip.dsw Normal file
View file

@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "wadzip"=.\wadzip.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

30
wadzip.txt Normal file
View file

@ -0,0 +1,30 @@
Compressed wad format:
Just like a normal wad, except:
1. The header id at the very beginning says "ZWAD"
instead of PWAD or IWAD.
2. The first four bytes of each lump are (little
endian) the uncompressed size. If this is zero,
the lump is not compressed, and you can subtract
four from the size given in the wadfile directory.
3. Unless those first four bytes give a value of
0, the lump is compressed with liblzf after that.
To compile this program, just compiling all its
source files should work, that is:
gcc *.c -o wadzip
[On a big-endian CPU, add "-DWORDS_BIGENDIAN" to
that command line.]
Visual C++ should also work, as well as gcc.
Compress a wad:
wadzip c original.wad compressed.wad
Decompress a wad (restoring the original):
wadzip d compressed.wad original.wad

76
xm.c Normal file
View file

@ -0,0 +1,76 @@
/* Fail-proof memory allocation by Graue <graue@oceanbase.org>; public domain */
#include <stdlib.h>
#include <limits.h>
#include "err.h"
/* MinGW (for example) appears to lack SIZE_T_MAX. */
#ifndef SIZE_T_MAX
#define SIZE_T_MAX ULONG_MAX
#endif
/*
* The usual xmalloc and xrealloc type routines, for allocating memory
* and bombing out if it fails.
*
* xm and xr here take two values: size and nmemb, like fread/fwrite.
* The idea is to be able to check for overflow, to prevent some weird
* crashes and security problems that can arise if you multiply size
* and nmemb and they overflow.
*/
/* FIXME: use %zu (size_t) in these format arguments if library is C99 */
/*
* Multiply size and number of elements, bombing out if overflow would
* occur.
* Note: In the case where size*nmemb is exactly equal to SIZE_T_MAX,
* this function considers that an overflow, even though it isn't
* really. But it's not likely the program will get that much memory,
* anyway.
*/
static size_t sizmul(size_t size, size_t nmemb)
{
if (SIZE_T_MAX / size <= nmemb)
{
errx(1, "allocating %lu objects of size %lu "
"would overflow", (unsigned long)nmemb,
(unsigned long)size);
}
return size * nmemb; /* won't overflow */
}
static void nomem(size_t size, size_t nmemb)
{
err(1, "cannot allocate %lu objects of size %lu "
"(total %lu bytes)", (unsigned long)nmemb,
(unsigned long)size, (unsigned long)(nmemb*size));
}
void *xm(size_t size, size_t nmemb)
{
const size_t bytes = sizmul(size, nmemb);
void *p;
p = malloc(bytes);
if (p == NULL) nomem(size, nmemb);
return p;
}
void *xr(void *p, size_t size, size_t nmemb)
{
const size_t bytes = sizmul(size, nmemb);
p = realloc(p, bytes);
if (p == NULL) nomem(size, nmemb);
return p;
}
/* expand array */
void *xpnd(void *p, int nit, int *sit, size_t sz)
{
if (nit < *sit) return p;
else if (*sit > 0) return xr(p, sz, (*sit *= 2));
else return xm(sz, (*sit = 10));
}

4
xm.h Normal file
View file

@ -0,0 +1,4 @@
void *xm(size_t size, size_t nmemb);
void *xr(void *p, size_t size, size_t nmemb);
void *xpnd(void *p, int nit, int *sit, size_t sz);
#define XPND(p, num, spc) (p = xpnd(p, num, &spc, sizeof *p))