added mpq archive support for the luls
updated plugin apis. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4325 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
ffc2a08589
commit
98d4e12ffc
8 changed files with 1435 additions and 37 deletions
|
@ -26,3 +26,7 @@ irc:
|
|||
irc-clean:
|
||||
$(MAKE) clean -C irc
|
||||
|
||||
native:
|
||||
@echo outdir = $(OUT_DIR)
|
||||
$(CC) -o $(OUT_DIR)/fteplug_avplugx86.dll -shared -Iavplug -Iavplug/libavformat -Iavplug/libavcodec -Iavplug/libavutil -Iavplug/libswscale -Iavplug/msvc_lib avplug/avencode.c avplug/avdecode.c plugin.def plugin.c -Lavplug/lib32 -lavcodec -lavformat -lavutil -lswscale -lwinmm
|
||||
$(CC) $(BASE_CFLAGS) -DFTEPLUGIN -o $(OUT_DIR)/fteplug_mpqx86.dll -shared -Impq mpq/fs_mpq.c mpq/blast.c plugin.def plugin.c qvm_api.c -Lavplug/lib32 -L../engine/libs/mingw-libs -lzlib
|
|
@ -1,8 +1,9 @@
|
|||
video encoder/decoder using the ffmpeg avformat/avcodec libraries.
|
||||
|
||||
The video decoder plugs into the media decoder functionality on media with an 'av:' prefix, specifically:
|
||||
The console command 'playfilm av:c:\foo.mpg' will start playing back c:\foo.mpg fullscreen.
|
||||
The shader term 'videomap av:c:\foo.mpg' will play the video upon the shader. This can be used with csqc+drawpic, csqc+beginpolygon, or placed upon walls.
|
||||
The video decoder plugs into the media decoder functionality on media with an 'av:' or 'avs:' prefix, specifically:
|
||||
The console command 'playfilm av:foo.mpg' will start playing back $BASEDIR/$GAMEDIR/foo.mpg fullscreen (or from inside paks/pk3s, but make sure seeking is fast, so avoid compression in pk3s...).
|
||||
The console command 'playfilm avs:c:\foo.mpg' will start playing back c:\foo.mpg fullscreen.
|
||||
The shader term 'videomap avs:c:\foo.mpg' will play the video upon the shader. This can be used with csqc+drawpic, csqc+beginpolygon, or placed upon walls.
|
||||
It theoretically supports any file that the avformat/avcodec libraries support, but has no ability to pass arguments, thus playback is likely limited only to files which require no explicit overrides.
|
||||
|
||||
The video encoder plugs into the existing capture command. Or something. I don't know. Its all basically junk.
|
||||
|
|
444
plugins/mpq/blast.c
Normal file
444
plugins/mpq/blast.c
Normal file
|
@ -0,0 +1,444 @@
|
|||
/* blast.c
|
||||
* Copyright (C) 2003 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in blast.h
|
||||
* version 1.1, 16 Feb 2003
|
||||
*
|
||||
* blast.c decompresses data compressed by the PKWare Compression Library.
|
||||
* This function provides functionality similar to the explode() function of
|
||||
* the PKWare library, hence the name "blast".
|
||||
*
|
||||
* This decompressor is based on the excellent format description provided by
|
||||
* Ben Rudiak-Gould in comp.compression on August 13, 2001. Interestingly, the
|
||||
* example Ben provided in the post is incorrect. The distance 110001 should
|
||||
* instead be 111000. When corrected, the example byte stream becomes:
|
||||
*
|
||||
* 00 04 82 24 25 8f 80 7f
|
||||
*
|
||||
* which decompresses to "AIAIAIAIAIAIA" (without the quotes).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Change history:
|
||||
*
|
||||
* 1.0 12 Feb 2003 - First version
|
||||
* 1.1 16 Feb 2003 - Fixed distance check for > 4 GB uncompressed data
|
||||
*/
|
||||
|
||||
#include <setjmp.h> /* for setjmp(), longjmp(), and jmp_buf */
|
||||
#include "blast.h" /* prototype for blast() */
|
||||
|
||||
#define local static /* for local function definitions */
|
||||
#define MAXBITS 13 /* maximum code length */
|
||||
#define MAXWIN 4096 /* maximum window size */
|
||||
|
||||
/* input and output state */
|
||||
struct state {
|
||||
/* input state */
|
||||
blast_in infun; /* input function provided by user */
|
||||
void *inhow; /* opaque information passed to infun() */
|
||||
unsigned char *in; /* next input location */
|
||||
unsigned left; /* available input at in */
|
||||
int bitbuf; /* bit buffer */
|
||||
int bitcnt; /* number of bits in bit buffer */
|
||||
|
||||
/* input limit error return state for bits() and decode() */
|
||||
jmp_buf env;
|
||||
|
||||
/* output state */
|
||||
blast_out outfun; /* output function provided by user */
|
||||
void *outhow; /* opaque information passed to outfun() */
|
||||
unsigned next; /* index of next write location in out[] */
|
||||
int first; /* true to check distances (for first 4K) */
|
||||
unsigned char out[MAXWIN]; /* output buffer and sliding window */
|
||||
};
|
||||
|
||||
/*
|
||||
* Return need bits from the input stream. This always leaves less than
|
||||
* eight bits in the buffer. bits() works properly for need == 0.
|
||||
*
|
||||
* Format notes:
|
||||
*
|
||||
* - Bits are stored in bytes from the least significant bit to the most
|
||||
* significant bit. Therefore bits are dropped from the bottom of the bit
|
||||
* buffer, using shift right, and new bytes are appended to the top of the
|
||||
* bit buffer, using shift left.
|
||||
*/
|
||||
local int bits(struct state *s, int need)
|
||||
{
|
||||
int val; /* bit accumulator */
|
||||
|
||||
/* load at least need bits into val */
|
||||
val = s->bitbuf;
|
||||
while (s->bitcnt < need) {
|
||||
if (s->left == 0) {
|
||||
s->left = s->infun(s->inhow, &(s->in));
|
||||
if (s->left == 0) longjmp(s->env, 1); /* out of input */
|
||||
}
|
||||
val |= (int)(*(s->in)++) << s->bitcnt; /* load eight bits */
|
||||
s->left--;
|
||||
s->bitcnt += 8;
|
||||
}
|
||||
|
||||
/* drop need bits and update buffer, always zero to seven bits left */
|
||||
s->bitbuf = val >> need;
|
||||
s->bitcnt -= need;
|
||||
|
||||
/* return need bits, zeroing the bits above that */
|
||||
return val & ((1 << need) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
|
||||
* each length, which for a canonical code are stepped through in order.
|
||||
* symbol[] are the symbol values in canonical order, where the number of
|
||||
* entries is the sum of the counts in count[]. The decoding process can be
|
||||
* seen in the function decode() below.
|
||||
*/
|
||||
struct huffman {
|
||||
short *count; /* number of symbols of each length */
|
||||
short *symbol; /* canonically ordered symbols */
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode a code from the stream s using huffman table h. Return the symbol or
|
||||
* a negative value if there is an error. If all of the lengths are zero, i.e.
|
||||
* an empty code, or if the code is incomplete and an invalid code is received,
|
||||
* then -9 is returned after reading MAXBITS bits.
|
||||
*
|
||||
* Format notes:
|
||||
*
|
||||
* - The codes as stored in the compressed data are bit-reversed relative to
|
||||
* a simple integer ordering of codes of the same lengths. Hence below the
|
||||
* bits are pulled from the compressed data one at a time and used to
|
||||
* build the code value reversed from what is in the stream in order to
|
||||
* permit simple integer comparisons for decoding.
|
||||
*
|
||||
* - The first code for the shortest length is all ones. Subsequent codes of
|
||||
* the same length are simply integer decrements of the previous code. When
|
||||
* moving up a length, a one bit is appended to the code. For a complete
|
||||
* code, the last code of the longest length will be all zeros. To support
|
||||
* this ordering, the bits pulled during decoding are inverted to apply the
|
||||
* more "natural" ordering starting with all zeros and incrementing.
|
||||
*/
|
||||
local int decode(struct state *s, struct huffman *h)
|
||||
{
|
||||
int len; /* current number of bits in code */
|
||||
int code; /* len bits being decoded */
|
||||
int first; /* first code of length len */
|
||||
int count; /* number of codes of length len */
|
||||
int index; /* index of first code of length len in symbol table */
|
||||
int bitbuf; /* bits from stream */
|
||||
int left; /* bits left in next or left to process */
|
||||
short *next; /* next number of codes */
|
||||
|
||||
bitbuf = s->bitbuf;
|
||||
left = s->bitcnt;
|
||||
code = first = index = 0;
|
||||
len = 1;
|
||||
next = h->count + 1;
|
||||
while (1) {
|
||||
while (left--) {
|
||||
code |= (bitbuf & 1) ^ 1; /* invert code */
|
||||
bitbuf >>= 1;
|
||||
count = *next++;
|
||||
if (code < first + count) { /* if length len, return symbol */
|
||||
s->bitbuf = bitbuf;
|
||||
s->bitcnt = (s->bitcnt - len) & 7;
|
||||
return h->symbol[index + (code - first)];
|
||||
}
|
||||
index += count; /* else update for next length */
|
||||
first += count;
|
||||
first <<= 1;
|
||||
code <<= 1;
|
||||
len++;
|
||||
}
|
||||
left = (MAXBITS+1) - len;
|
||||
if (left == 0) break;
|
||||
if (s->left == 0) {
|
||||
s->left = s->infun(s->inhow, &(s->in));
|
||||
if (s->left == 0) longjmp(s->env, 1); /* out of input */
|
||||
}
|
||||
bitbuf = *(s->in)++;
|
||||
s->left--;
|
||||
if (left > 8) left = 8;
|
||||
}
|
||||
return -9; /* ran out of codes */
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a list of repeated code lengths rep[0..n-1], where each byte is a
|
||||
* count (high four bits + 1) and a code length (low four bits), generate the
|
||||
* list of code lengths. This compaction reduces the size of the object code.
|
||||
* Then given the list of code lengths length[0..n-1] representing a canonical
|
||||
* Huffman code for n symbols, construct the tables required to decode those
|
||||
* codes. Those tables are the number of codes of each length, and the symbols
|
||||
* sorted by length, retaining their original order within each length. The
|
||||
* return value is zero for a complete code set, negative for an over-
|
||||
* subscribed code set, and positive for an incomplete code set. The tables
|
||||
* can be used if the return value is zero or positive, but they cannot be used
|
||||
* if the return value is negative. If the return value is zero, it is not
|
||||
* possible for decode() using that table to return an error--any stream of
|
||||
* enough bits will resolve to a symbol. If the return value is positive, then
|
||||
* it is possible for decode() using that table to return an error for received
|
||||
* codes past the end of the incomplete lengths.
|
||||
*/
|
||||
local int construct(struct huffman *h, const unsigned char *rep, int n)
|
||||
{
|
||||
int symbol; /* current symbol when stepping through length[] */
|
||||
int len; /* current length when stepping through h->count[] */
|
||||
int left; /* number of possible codes left of current length */
|
||||
short offs[MAXBITS+1]; /* offsets in symbol table for each length */
|
||||
short length[256]; /* code lengths */
|
||||
|
||||
/* convert compact repeat counts into symbol bit length list */
|
||||
symbol = 0;
|
||||
do {
|
||||
len = *rep++;
|
||||
left = (len >> 4) + 1;
|
||||
len &= 15;
|
||||
do {
|
||||
length[symbol++] = len;
|
||||
} while (--left);
|
||||
} while (--n);
|
||||
n = symbol;
|
||||
|
||||
/* count number of codes of each length */
|
||||
for (len = 0; len <= MAXBITS; len++)
|
||||
h->count[len] = 0;
|
||||
for (symbol = 0; symbol < n; symbol++)
|
||||
(h->count[length[symbol]])++; /* assumes lengths are within bounds */
|
||||
if (h->count[0] == n) /* no codes! */
|
||||
return 0; /* complete, but decode() will fail */
|
||||
|
||||
/* check for an over-subscribed or incomplete set of lengths */
|
||||
left = 1; /* one possible code of zero length */
|
||||
for (len = 1; len <= MAXBITS; len++) {
|
||||
left <<= 1; /* one more bit, double codes left */
|
||||
left -= h->count[len]; /* deduct count from possible codes */
|
||||
if (left < 0) return left; /* over-subscribed--return negative */
|
||||
} /* left > 0 means incomplete */
|
||||
|
||||
/* generate offsets into symbol table for each length for sorting */
|
||||
offs[1] = 0;
|
||||
for (len = 1; len < MAXBITS; len++)
|
||||
offs[len + 1] = offs[len] + h->count[len];
|
||||
|
||||
/*
|
||||
* put symbols in table sorted by length, by symbol order within each
|
||||
* length
|
||||
*/
|
||||
for (symbol = 0; symbol < n; symbol++)
|
||||
if (length[symbol] != 0)
|
||||
h->symbol[offs[length[symbol]]++] = symbol;
|
||||
|
||||
/* return zero for complete set, positive for incomplete set */
|
||||
return left;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode PKWare Compression Library stream.
|
||||
*
|
||||
* Format notes:
|
||||
*
|
||||
* - First byte is 0 if literals are uncoded or 1 if they are coded. Second
|
||||
* byte is 4, 5, or 6 for the number of extra bits in the distance code.
|
||||
* This is the base-2 logarithm of the dictionary size minus six.
|
||||
*
|
||||
* - Compressed data is a combination of literals and length/distance pairs
|
||||
* terminated by an end code. Literals are either Huffman coded or
|
||||
* uncoded bytes. A length/distance pair is a coded length followed by a
|
||||
* coded distance to represent a string that occurs earlier in the
|
||||
* uncompressed data that occurs again at the current location.
|
||||
*
|
||||
* - A bit preceding a literal or length/distance pair indicates which comes
|
||||
* next, 0 for literals, 1 for length/distance.
|
||||
*
|
||||
* - If literals are uncoded, then the next eight bits are the literal, in the
|
||||
* normal bit order in th stream, i.e. no bit-reversal is needed. Similarly,
|
||||
* no bit reversal is needed for either the length extra bits or the distance
|
||||
* extra bits.
|
||||
*
|
||||
* - Literal bytes are simply written to the output. A length/distance pair is
|
||||
* an instruction to copy previously uncompressed bytes to the output. The
|
||||
* copy is from distance bytes back in the output stream, copying for length
|
||||
* bytes.
|
||||
*
|
||||
* - Distances pointing before the beginning of the output data are not
|
||||
* permitted.
|
||||
*
|
||||
* - Overlapped copies, where the length is greater than the distance, are
|
||||
* allowed and common. For example, a distance of one and a length of 518
|
||||
* simply copies the last byte 518 times. A distance of four and a length of
|
||||
* twelve copies the last four bytes three times. A simple forward copy
|
||||
* ignoring whether the length is greater than the distance or not implements
|
||||
* this correctly.
|
||||
*/
|
||||
local int decomp(struct state *s)
|
||||
{
|
||||
int lit; /* true if literals are coded */
|
||||
int dict; /* log2(dictionary size) - 6 */
|
||||
int symbol; /* decoded symbol, extra bits for distance */
|
||||
int len; /* length for copy */
|
||||
int dist; /* distance for copy */
|
||||
int copy; /* copy counter */
|
||||
unsigned char *from, *to; /* copy pointers */
|
||||
static int virgin = 1; /* build tables once */
|
||||
static short litcnt[MAXBITS+1], litsym[256]; /* litcode memory */
|
||||
static short lencnt[MAXBITS+1], lensym[16]; /* lencode memory */
|
||||
static short distcnt[MAXBITS+1], distsym[64]; /* distcode memory */
|
||||
static struct huffman litcode = {litcnt, litsym}; /* length code */
|
||||
static struct huffman lencode = {lencnt, lensym}; /* length code */
|
||||
static struct huffman distcode = {distcnt, distsym};/* distance code */
|
||||
/* bit lengths of literal codes */
|
||||
static const unsigned char litlen[] = {
|
||||
11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,
|
||||
9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
|
||||
7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
|
||||
8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
|
||||
44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,
|
||||
44, 173};
|
||||
/* bit lengths of length codes 0..15 */
|
||||
static const unsigned char lenlen[] = {2, 35, 36, 53, 38, 23};
|
||||
/* bit lengths of distance codes 0..63 */
|
||||
static const unsigned char distlen[] = {2, 20, 53, 230, 247, 151, 248};
|
||||
static const short base[16] = { /* base for length codes */
|
||||
3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264};
|
||||
static const char extra[16] = { /* extra bits for length codes */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
/* set up decoding tables (once--might not be thread-safe) */
|
||||
if (virgin) {
|
||||
construct(&litcode, litlen, sizeof(litlen));
|
||||
construct(&lencode, lenlen, sizeof(lenlen));
|
||||
construct(&distcode, distlen, sizeof(distlen));
|
||||
virgin = 0;
|
||||
}
|
||||
|
||||
/* read header */
|
||||
lit = bits(s, 8);
|
||||
if (lit > 1) return -1;
|
||||
dict = bits(s, 8);
|
||||
if (dict < 4 || dict > 6) return -2;
|
||||
|
||||
/* decode literals and length/distance pairs */
|
||||
do {
|
||||
if (bits(s, 1)) {
|
||||
/* get length */
|
||||
symbol = decode(s, &lencode);
|
||||
len = base[symbol] + bits(s, extra[symbol]);
|
||||
if (len == 519) break; /* end code */
|
||||
|
||||
/* get distance */
|
||||
symbol = len == 2 ? 2 : dict;
|
||||
dist = decode(s, &distcode) << symbol;
|
||||
dist += bits(s, symbol);
|
||||
dist++;
|
||||
if (s->first && dist > s->next)
|
||||
return -3; /* distance too far back */
|
||||
|
||||
/* copy length bytes from distance bytes back */
|
||||
do {
|
||||
to = s->out + s->next;
|
||||
from = to - dist;
|
||||
copy = MAXWIN;
|
||||
if (s->next < dist) {
|
||||
from += copy;
|
||||
copy = dist;
|
||||
}
|
||||
copy -= s->next;
|
||||
if (copy > len) copy = len;
|
||||
len -= copy;
|
||||
s->next += copy;
|
||||
do {
|
||||
*to++ = *from++;
|
||||
} while (--copy);
|
||||
if (s->next == MAXWIN) {
|
||||
if (s->outfun(s->outhow, s->out, s->next)) return 1;
|
||||
s->next = 0;
|
||||
s->first = 0;
|
||||
}
|
||||
} while (len != 0);
|
||||
}
|
||||
else {
|
||||
/* get literal and write it */
|
||||
symbol = lit ? decode(s, &litcode) : bits(s, 8);
|
||||
s->out[s->next++] = symbol;
|
||||
if (s->next == MAXWIN) {
|
||||
if (s->outfun(s->outhow, s->out, s->next)) return 1;
|
||||
s->next = 0;
|
||||
s->first = 0;
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See comments in blast.h */
|
||||
int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow)
|
||||
{
|
||||
struct state s; /* input/output state */
|
||||
int err; /* return value */
|
||||
|
||||
/* initialize input state */
|
||||
s.infun = infun;
|
||||
s.inhow = inhow;
|
||||
s.left = 0;
|
||||
s.bitbuf = 0;
|
||||
s.bitcnt = 0;
|
||||
|
||||
/* initialize output state */
|
||||
s.outfun = outfun;
|
||||
s.outhow = outhow;
|
||||
s.next = 0;
|
||||
s.first = 1;
|
||||
|
||||
/* return if bits() or decode() tries to read past available input */
|
||||
if (setjmp(s.env) != 0) /* if came back here via longjmp(), */
|
||||
err = 2; /* then skip decomp(), return error */
|
||||
else
|
||||
err = decomp(&s); /* decompress */
|
||||
|
||||
/* write any leftover output and update the error code if needed */
|
||||
if (err != 1 && s.next && s.outfun(s.outhow, s.out, s.next) && err == 0)
|
||||
err = 1;
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
/* Example of how to use blast() */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define CHUNK 16384
|
||||
|
||||
local unsigned inf(void *how, unsigned char **buf)
|
||||
{
|
||||
static unsigned char hold[CHUNK];
|
||||
|
||||
*buf = hold;
|
||||
return fread(hold, 1, CHUNK, (FILE *)how);
|
||||
}
|
||||
|
||||
local int outf(void *how, unsigned char *buf, unsigned len)
|
||||
{
|
||||
return fwrite(buf, 1, len, (FILE *)how) != len;
|
||||
}
|
||||
|
||||
/* Decompress a PKWare Compression Library stream from stdin to stdout */
|
||||
int main(void)
|
||||
{
|
||||
int ret, n;
|
||||
|
||||
/* decompress to stdout */
|
||||
ret = blast(inf, stdin, outf, stdout);
|
||||
if (ret != 0) fprintf(stderr, "blast error: %d\n", ret);
|
||||
|
||||
/* see if there are any leftover bytes */
|
||||
n = 0;
|
||||
while (getchar() != EOF) n++;
|
||||
if (n) fprintf(stderr, "blast warning: %d unused bytes of input\n", n);
|
||||
|
||||
/* return blast() error code */
|
||||
return ret;
|
||||
}
|
||||
#endif
|
71
plugins/mpq/blast.h
Normal file
71
plugins/mpq/blast.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* blast.h -- interface for blast.c
|
||||
Copyright (C) 2003 Mark Adler
|
||||
version 1.1, 16 Feb 2003
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Mark Adler madler@alumni.caltech.edu
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* blast() decompresses the PKWare Data Compression Library (DCL) compressed
|
||||
* format. It provides the same functionality as the explode() function in
|
||||
* that library. (Note: PKWare overused the "implode" verb, and the format
|
||||
* used by their library implode() function is completely different and
|
||||
* incompatible with the implode compression method supported by PKZIP.)
|
||||
*/
|
||||
|
||||
|
||||
typedef unsigned (*blast_in)(void *how, unsigned char **buf);
|
||||
typedef int (*blast_out)(void *how, unsigned char *buf, unsigned len);
|
||||
/* Definitions for input/output functions passed to blast(). See below for
|
||||
* what the provided functions need to do.
|
||||
*/
|
||||
|
||||
|
||||
int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow);
|
||||
/* Decompress input to output using the provided infun() and outfun() calls.
|
||||
* On success, the return value of blast() is zero. If there is an error in
|
||||
* the source data, i.e. it is not in the proper format, then a negative value
|
||||
* is returned. If there is not enough input available or there is not enough
|
||||
* output space, then a positive error is returned.
|
||||
*
|
||||
* The input function is invoked: len = infun(how, &buf), where buf is set by
|
||||
* infun() to point to the input buffer, and infun() returns the number of
|
||||
* available bytes there. If infun() returns zero, then blast() returns with
|
||||
* an input error. (blast() only asks for input if it needs it.) inhow is for
|
||||
* use by the application to pass an input descriptor to infun(), if desired.
|
||||
*
|
||||
* The output function is invoked: err = outfun(how, buf, len), where the bytes
|
||||
* to be written are buf[0..len-1]. If err is not zero, then blast() returns
|
||||
* with an output error. outfun() is always called with len <= 4096. outhow
|
||||
* is for use by the application to pass an output descriptor to outfun(), if
|
||||
* desired.
|
||||
*
|
||||
* The return codes are:
|
||||
*
|
||||
* 2: ran out of input before completing decompression
|
||||
* 1: output error before completing decompression
|
||||
* 0: successful decompression
|
||||
* -1: literal flag not zero or one
|
||||
* -2: dictionary size not in 4..6
|
||||
* -3: distance is too far back
|
||||
*
|
||||
* At the bottom of blast.c is an example program that uses blast() that can be
|
||||
* compiled to produce a command-line decompression filter by defining TEST.
|
||||
*/
|
844
plugins/mpq/fs_mpq.c
Normal file
844
plugins/mpq/fs_mpq.c
Normal file
|
@ -0,0 +1,844 @@
|
|||
#include "quakedef.h"
|
||||
#include "../plugin.h"
|
||||
//#include "../engine.h"
|
||||
#include "fs.h"
|
||||
#include <assert.h>
|
||||
|
||||
#include "../../engine/libs/zlib.h"
|
||||
#include "blast.h"
|
||||
|
||||
//http://bazaar.launchpad.net/~jeanfrancois.roy/mpqkit/trunk/files
|
||||
//http://www.zezula.net/en/mpq/main.html
|
||||
//http://www.wc3c.net/tools/specs/QuantamMPQFormat.txt
|
||||
|
||||
typedef unsigned long long ofs_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char mpq_magic[4];
|
||||
unsigned int header_size;
|
||||
unsigned int archive_size;
|
||||
unsigned short version;
|
||||
unsigned short sector_size_shift;
|
||||
unsigned int hash_table_offset;
|
||||
unsigned int block_table_offset;
|
||||
unsigned int hash_table_length;
|
||||
unsigned int block_table_length;
|
||||
} mpqheader_t;
|
||||
|
||||
enum
|
||||
{
|
||||
MPQFileValid = 0x80000000,
|
||||
MPQFileHasSectorAdlers = 0x04000000,
|
||||
MPQFileStopSearchMarker = 0x02000000,
|
||||
MPQFileOneSector = 0x01000000,
|
||||
MPQFilePatch = 0x00100000,
|
||||
MPQFileOffsetAdjustedKey = 0x00020000,
|
||||
MPQFileEncrypted = 0x00010000,
|
||||
MPQFileCompressed = 0x00000200,
|
||||
MPQFileDiabloCompressed = 0x00000100,
|
||||
MPQFileFlagsMask = 0x87030300
|
||||
};
|
||||
typedef struct
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int archived_size;
|
||||
unsigned int size;
|
||||
unsigned int flags;
|
||||
} mpqblock_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int hash_a;
|
||||
unsigned int hash_b;
|
||||
unsigned short locale;
|
||||
unsigned short platform;
|
||||
unsigned int block_table_index;
|
||||
} mpqhash_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char desc[MAX_OSPATH];
|
||||
vfsfile_t *file;
|
||||
ofs_t filestart;
|
||||
ofs_t fileend;
|
||||
|
||||
unsigned int sectorsize;
|
||||
|
||||
mpqhash_t *hashdata;
|
||||
unsigned int hashentries;
|
||||
|
||||
mpqblock_t *blockdata;
|
||||
unsigned int blockentries;
|
||||
|
||||
char *listfile;
|
||||
|
||||
mpqheader_t header_0;
|
||||
struct
|
||||
{
|
||||
unsigned long long extended_block_offset_table_offset;
|
||||
unsigned short hash_table_offset_high;
|
||||
unsigned short block_table_offset_high;
|
||||
} header_1;
|
||||
} mpqarchive_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vfsfile_t funcs;
|
||||
unsigned int flags;
|
||||
unsigned int encryptkey;
|
||||
mpqarchive_t *archive;
|
||||
unsigned int foffset;
|
||||
unsigned int flength;
|
||||
unsigned int alength;
|
||||
|
||||
ofs_t archiveoffset;
|
||||
|
||||
unsigned int buffersect;
|
||||
unsigned int bufferlength;
|
||||
char *buffer;
|
||||
unsigned int *sectortab;
|
||||
} mpqfile_t;
|
||||
|
||||
|
||||
static qboolean crypt_table_initialized = false;
|
||||
static unsigned int crypt_table[0x500];
|
||||
|
||||
void mpq_init_cryptography(void)
|
||||
{
|
||||
// prepare crypt_table
|
||||
unsigned int seed = 0x00100001;
|
||||
unsigned int index1 = 0;
|
||||
unsigned int index2 = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (!crypt_table_initialized)
|
||||
{
|
||||
crypt_table_initialized = true;
|
||||
|
||||
for (index1 = 0; index1 < 0x100; index1++)
|
||||
{
|
||||
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
|
||||
{
|
||||
unsigned int temp1, temp2;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp1 = (seed & 0xFFFF) << 0x10;
|
||||
|
||||
seed = (seed * 125 + 3) % 0x2AAAAB;
|
||||
temp2 = (seed & 0xFFFF);
|
||||
|
||||
crypt_table[index2] = (temp1 | temp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define HASH_POSITION 0
|
||||
#define HASH_NAME_A 1
|
||||
#define HASH_NAME_B 2
|
||||
#define HASH_KEY 3
|
||||
unsigned int mpq_hash_cstring(const char *string, unsigned int type)
|
||||
{
|
||||
unsigned int seed1 = 0x7FED7FED;
|
||||
unsigned int seed2 = 0xEEEEEEEE;
|
||||
unsigned int shifted_type = (type << 8);
|
||||
unsigned int ch;
|
||||
|
||||
assert(crypt_table_initialized);
|
||||
assert(string);
|
||||
|
||||
while (*string != 0)
|
||||
{
|
||||
ch = *string++;
|
||||
if (ch == '/')
|
||||
ch = '\\';
|
||||
if (ch > 0x60 && ch < 0x7b) ch -= 0x20;
|
||||
|
||||
seed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2);
|
||||
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
|
||||
}
|
||||
|
||||
return seed1;
|
||||
}
|
||||
|
||||
#define MPQSwapInt32LittleToHost(a) (a)
|
||||
#define MPQSwapInt32HostToLittle(a) (a)
|
||||
|
||||
void mpq_decrypt(void* data, size_t length, unsigned int key, qboolean disable_output_swapping)
|
||||
{
|
||||
unsigned int* buffer32 = (unsigned int*)data;
|
||||
unsigned int seed = 0xEEEEEEEE;
|
||||
unsigned int ch;
|
||||
|
||||
assert(crypt_table_initialized);
|
||||
assert(data);
|
||||
|
||||
// round to 4 bytes
|
||||
length = length / 4;
|
||||
|
||||
if (disable_output_swapping)
|
||||
{
|
||||
while (length-- > 0)
|
||||
{
|
||||
ch = MPQSwapInt32LittleToHost(*buffer32);
|
||||
|
||||
seed += crypt_table[0x400 + (key & 0xFF)];
|
||||
ch = ch ^ (key + seed);
|
||||
|
||||
key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B);
|
||||
seed = ch + seed + (seed << 5) + 3;
|
||||
|
||||
*buffer32++ = ch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (length-- > 0)
|
||||
{
|
||||
ch = MPQSwapInt32LittleToHost(*buffer32);
|
||||
|
||||
seed += crypt_table[0x400 + (key & 0xFF)];
|
||||
ch = ch ^ (key + seed);
|
||||
|
||||
key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B);
|
||||
seed = ch + seed + (seed << 5) + 3;
|
||||
|
||||
*buffer32++ = MPQSwapInt32HostToLittle(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define HASH_TABLE_EMPTY 0xffffffff
|
||||
#define HASH_TABLE_DELETED 0xfffffffe
|
||||
unsigned int mpq_lookuphash(mpqarchive_t *mpq, const char *filename, int locale)
|
||||
{
|
||||
unsigned int initial_position = mpq_hash_cstring(filename, HASH_POSITION) % mpq->hashentries;
|
||||
unsigned int current_position = initial_position;
|
||||
unsigned int hash_a = mpq_hash_cstring(filename, HASH_NAME_A);
|
||||
unsigned int hash_b = mpq_hash_cstring(filename, HASH_NAME_B);
|
||||
|
||||
// Search through the hash table until we either find the file we're looking for, or we find an unused hash table entry,
|
||||
// indicating the end of the cluster of used hash table entries
|
||||
while (mpq->hashdata[current_position].block_table_index != HASH_TABLE_EMPTY)
|
||||
{
|
||||
if (mpq->hashdata[current_position].block_table_index != HASH_TABLE_DELETED)
|
||||
{
|
||||
if (mpq->hashdata[current_position].hash_a == hash_a &&
|
||||
mpq->hashdata[current_position].hash_b == hash_b &&
|
||||
mpq->hashdata[current_position].locale == locale)
|
||||
{
|
||||
return current_position;
|
||||
}
|
||||
}
|
||||
|
||||
current_position++;
|
||||
current_position %= mpq->hashentries;
|
||||
|
||||
//avoid infinity
|
||||
if (current_position == initial_position)
|
||||
break;
|
||||
}
|
||||
|
||||
return HASH_TABLE_EMPTY;
|
||||
}
|
||||
|
||||
vfsfile_t *MPQ_OpenVFS(void *handle, flocation_t *loc, const char *mode);
|
||||
void MPQ_GetDisplayPath(void *handle, char *outpath, unsigned int pathsize)
|
||||
{
|
||||
mpqarchive_t *mpq = handle;
|
||||
strlcpy(outpath, mpq->desc, pathsize);
|
||||
}
|
||||
void MPQ_ClosePath(void *handle)
|
||||
{
|
||||
mpqarchive_t *mpq = handle;
|
||||
VFS_CLOSE(mpq->file);
|
||||
free(mpq->blockdata);
|
||||
free(mpq->hashdata);
|
||||
free(mpq->listfile);
|
||||
free(mpq);
|
||||
}
|
||||
qboolean MPQ_FindFile(void *handle, flocation_t *loc, const char *name, void *hashedresult)
|
||||
{
|
||||
mpqarchive_t *mpq = handle;
|
||||
unsigned int hashentry;
|
||||
unsigned int blockentry;
|
||||
|
||||
hashentry = mpq_lookuphash(handle, name, 0);
|
||||
if (hashentry == HASH_TABLE_EMPTY)
|
||||
return false;
|
||||
blockentry = mpq->hashdata[hashentry].block_table_index;
|
||||
if (blockentry > mpq->blockentries)
|
||||
return false;
|
||||
if (loc)
|
||||
{
|
||||
loc->index = blockentry;
|
||||
loc->offset = 0;
|
||||
*loc->rawname = 0;
|
||||
loc->len = mpq->blockdata[blockentry].size;
|
||||
// loc->foo = foo;
|
||||
}
|
||||
|
||||
if (mpq->blockdata[blockentry].flags & MPQFilePatch)
|
||||
{
|
||||
Con_DPrintf("Cannot cope with patch files\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void MPQ_ReadFile(void *handle, flocation_t *loc, char *buffer)
|
||||
{
|
||||
vfsfile_t *f;
|
||||
f = MPQ_OpenVFS(handle, loc, "rb");
|
||||
if (!f) //err...
|
||||
return;
|
||||
VFS_READ(f, buffer, loc->len);
|
||||
VFS_CLOSE(f);
|
||||
}
|
||||
|
||||
static int mpqwildcmp(const char *wild, const char *string, char **end)
|
||||
{
|
||||
while (*string)
|
||||
{
|
||||
if (*string == '\r' || *string == '\n' || *string == ';')
|
||||
break;
|
||||
|
||||
if (*wild == '*')
|
||||
{
|
||||
if (wild[1] == *string || *string == '/' || *string == '\\')
|
||||
{
|
||||
//* terminates if we get a match on the char following it, or if its a \ or / char
|
||||
wild++;
|
||||
continue;
|
||||
}
|
||||
string++;
|
||||
}
|
||||
else if ((*wild == *string) || (*wild == '?'))
|
||||
{
|
||||
//this char matches
|
||||
wild++;
|
||||
string++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//failure
|
||||
while (*string && *string != '\r' && *string != '\n' && *string != ';')
|
||||
string++;
|
||||
*end = (char*)string;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*end = (char*)string;
|
||||
|
||||
while (*wild == '*')
|
||||
{
|
||||
wild++;
|
||||
}
|
||||
return !*wild;
|
||||
}
|
||||
int MPQ_EnumerateFiles(void *handle, const char *match, int (QDECL *func)(const char *fname, int fsize, void *parm, void *spath), void *parm)
|
||||
{
|
||||
int ok = 1;
|
||||
char *s, *n;
|
||||
char name[MAX_QPATH];
|
||||
flocation_t loc;
|
||||
mpqarchive_t *mpq = handle;
|
||||
if (mpq->listfile)
|
||||
{
|
||||
s = mpq->listfile;
|
||||
for (s = mpq->listfile; *s && ok; s = n)
|
||||
{
|
||||
if (mpqwildcmp(match, s, &n) && n - s < MAX_QPATH-1)
|
||||
{
|
||||
memcpy(name, s, n - s);
|
||||
name[n-s] = 0;
|
||||
|
||||
if (!MPQ_FindFile(handle, &loc, name, NULL))
|
||||
loc.len = 0;
|
||||
ok = func(name, loc.len, parm, handle);
|
||||
}
|
||||
|
||||
while (*n == '\n' || *n == '\r' || *n == ';')
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
void MPQ_BuildHash(void *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
|
||||
{
|
||||
mpqarchive_t *wp = handle;
|
||||
char *s, *n;
|
||||
char name[MAX_QPATH];
|
||||
mpqarchive_t *mpq = handle;
|
||||
if (mpq->listfile)
|
||||
{
|
||||
s = mpq->listfile;
|
||||
for (s = mpq->listfile; ; s = n)
|
||||
{
|
||||
while (*s == '\n' || *s == '\r' || *s == ';')
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
n = s;
|
||||
while (*n && *n != '\r' && *n != '\n' && *n != ';')
|
||||
n++;
|
||||
|
||||
memcpy(name, s, n - s);
|
||||
name[n-s] = 0;
|
||||
|
||||
AddFileHash(depth, name, NULL, wp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *MPQ_OpenNew(vfsfile_t *file, const char *desc)
|
||||
{
|
||||
flocation_t lloc;
|
||||
mpqarchive_t *mpq;
|
||||
mpqheader_t header;
|
||||
ofs_t block_ofs;
|
||||
ofs_t hash_ofs;
|
||||
|
||||
VFS_SEEK(file, 0);
|
||||
|
||||
if (VFS_READ(file, &header, sizeof(header)) != sizeof(header))
|
||||
return NULL;
|
||||
|
||||
if (memcmp(header.mpq_magic, "MPQ\x1a", 4))
|
||||
return NULL;
|
||||
|
||||
mpq = malloc(sizeof(*mpq));
|
||||
memset(mpq, 0, sizeof(*mpq));
|
||||
strlcpy(mpq->desc, desc, sizeof(mpq->desc));
|
||||
mpq->header_0 = header;
|
||||
mpq->file = file;
|
||||
mpq->filestart = 0;
|
||||
mpq->sectorsize = 512 << mpq->header_0.sector_size_shift;
|
||||
|
||||
block_ofs = header.block_table_offset;
|
||||
hash_ofs = header.hash_table_offset;
|
||||
if (header.version >= 1)
|
||||
{
|
||||
VFS_READ(file, &mpq->header_1, sizeof(mpq->header_1));
|
||||
}
|
||||
block_ofs |= ((ofs_t)mpq->header_1.block_table_offset_high)<<32u;
|
||||
hash_ofs |= ((ofs_t)mpq->header_1.hash_table_offset_high)<<32u;
|
||||
|
||||
mpq->fileend = VFS_GETLEN(file);
|
||||
|
||||
if (block_ofs + sizeof(*mpq->blockdata) * mpq->blockentries > mpq->fileend ||
|
||||
hash_ofs + sizeof(*mpq->hashdata) * mpq->hashentries > mpq->fileend)
|
||||
{
|
||||
Con_Printf("\"%s\" appears truncated\n", desc);
|
||||
free(mpq);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
mpq->hashentries = mpq->header_0.hash_table_length;
|
||||
mpq->hashdata = malloc(sizeof(*mpq->hashdata) * mpq->hashentries);
|
||||
VFS_SEEK(file, hash_ofs);
|
||||
VFS_READ(file, mpq->hashdata, sizeof(*mpq->hashdata) * mpq->hashentries);
|
||||
mpq_decrypt(mpq->hashdata, sizeof(*mpq->hashdata) * mpq->hashentries, mpq_hash_cstring("(hash table)", HASH_KEY), false);
|
||||
|
||||
mpq->blockentries = mpq->header_0.block_table_length;
|
||||
mpq->blockdata = malloc(sizeof(*mpq->blockdata) * mpq->blockentries);
|
||||
VFS_SEEK(file, block_ofs);
|
||||
VFS_READ(file, mpq->blockdata, sizeof(*mpq->blockdata) * mpq->blockentries);
|
||||
mpq_decrypt(mpq->blockdata, sizeof(*mpq->blockdata) * mpq->blockentries, mpq_hash_cstring("(block table)", HASH_KEY), true);
|
||||
|
||||
/*for (i = 0; i < mpq->header_0.block_table_length; i++)
|
||||
{
|
||||
Con_Printf("offset = %08x, csize = %i, usize=%i, flags=%s%s%s%s%s%s%s%s%s\n", mpq->blockdata[i].offset, mpq->blockdata[i].archived_size, mpq->blockdata[i].size,
|
||||
(mpq->blockdata[i].flags & MPQFileValid)?"valid ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileHasSectorAdlers)?"sectoradlers ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileStopSearchMarker)?"stopsearch ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileOneSector)?"singlesector ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileOffsetAdjustedKey)?"offsetadjust ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileEncrypted)?"encrypted ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileCompressed)?"compressed ":"",
|
||||
(mpq->blockdata[i].flags & MPQFileDiabloCompressed)?"dcompressed ":"",
|
||||
(mpq->blockdata[i].flags & ~MPQFileFlagsMask)?"OTHERS ":""
|
||||
);
|
||||
}*/
|
||||
|
||||
if (MPQ_FindFile(mpq, &lloc, "(listfile)", NULL))
|
||||
{
|
||||
char *bs;
|
||||
mpq->listfile = malloc(lloc.len+2);
|
||||
mpq->listfile[0] = 0;
|
||||
mpq->listfile[lloc.len] = 0;
|
||||
mpq->listfile[lloc.len+1] = 0;
|
||||
MPQ_ReadFile(mpq, &lloc, mpq->listfile);
|
||||
bs = mpq->listfile;
|
||||
while(1)
|
||||
{
|
||||
bs = strchr(bs, '\\');
|
||||
if (bs)
|
||||
*bs++ = '/';
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mpq;
|
||||
}
|
||||
|
||||
int MPQ_GeneratePureCRC (void *handle, int seed, int usepure)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct blastdata_s
|
||||
{
|
||||
void *outdata;
|
||||
unsigned int outlen;
|
||||
void *indata;
|
||||
unsigned int inlen;
|
||||
};
|
||||
unsigned mpqf_blastin(void *how, unsigned char **buf)
|
||||
{
|
||||
struct blastdata_s *args = how;
|
||||
*buf = args->indata;
|
||||
return args->inlen;
|
||||
}
|
||||
int mpqf_blastout(void *how, unsigned char *buf, unsigned len)
|
||||
{
|
||||
struct blastdata_s *args = how;
|
||||
if (len > args->outlen)
|
||||
return 1;
|
||||
memcpy(args->outdata, buf, len);
|
||||
args->outdata = (char*)args->outdata + len;
|
||||
args->outlen -= len;
|
||||
return 0;
|
||||
}
|
||||
void MPQF_decompress(qboolean legacymethod, void *outdata, unsigned int outlen, void *indata, unsigned int inlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
int methods;
|
||||
if (legacymethod)
|
||||
methods = 8;
|
||||
else
|
||||
{
|
||||
methods = *(unsigned char*)indata;
|
||||
indata = (char*)indata + 1;
|
||||
inlen--;
|
||||
}
|
||||
|
||||
if (methods == 8)
|
||||
{
|
||||
struct blastdata_s args = {outdata, outlen, indata, inlen};
|
||||
blast(mpqf_blastin, &args, mpqf_blastout, &args);
|
||||
}
|
||||
else if (methods == 2)
|
||||
{
|
||||
z_stream strm =
|
||||
{
|
||||
indata,
|
||||
inlen,
|
||||
0,
|
||||
|
||||
outdata,
|
||||
outlen,
|
||||
0,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
Z_UNKNOWN,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
inflateInit2(&strm, MAX_WBITS);
|
||||
|
||||
while ((ret=inflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)
|
||||
{
|
||||
if (strm.avail_in == 0 || strm.avail_out == 0)
|
||||
{
|
||||
if (strm.avail_in == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (strm.avail_out == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//doh, it terminated for no reason
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
inflateEnd(&strm);
|
||||
Con_Printf("Couldn't decompress gz file\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inflateEnd(&strm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("mpq: unsupported decompression method - %x\n", methods);
|
||||
memset(outdata, 0, outlen);
|
||||
}
|
||||
}
|
||||
|
||||
int MPQF_readbytes (struct vfsfile_s *file, void *buffer, int bytestoread)
|
||||
{
|
||||
int bytesread = 0;
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
|
||||
if (bytestoread + f->foffset > f->flength)
|
||||
bytestoread = f->flength - f->foffset;
|
||||
if (bytestoread < 0)
|
||||
return 0;
|
||||
|
||||
if (!(f->flags & (MPQFileCompressed|MPQFileDiabloCompressed)))
|
||||
{
|
||||
//no compression, just a raw file.
|
||||
VFS_SEEK(f->archive->file, f->archiveoffset + f->foffset);
|
||||
bytesread = VFS_READ(f->archive->file, buffer, bytestoread);
|
||||
f->foffset += bytesread;
|
||||
}
|
||||
else if (f->flags & MPQFileOneSector)
|
||||
{
|
||||
//fairly simple packed data, no sector nonsense. decode in one go
|
||||
if (!f->buffer)
|
||||
{
|
||||
char *cdata = malloc(f->alength);
|
||||
f->buffer = malloc(f->flength);
|
||||
VFS_SEEK(f->archive->file, f->archiveoffset);
|
||||
VFS_READ(f->archive->file, cdata, f->alength);
|
||||
|
||||
if (f->flags & MPQFileEncrypted)
|
||||
{
|
||||
mpq_decrypt(cdata, f->alength, f->encryptkey, false);
|
||||
}
|
||||
if (f->flags & (MPQFileCompressed|MPQFileDiabloCompressed))
|
||||
{
|
||||
//decompress
|
||||
MPQF_decompress(!!(f->flags&MPQFileDiabloCompressed), f->buffer, f->flength, cdata, f->alength);
|
||||
}
|
||||
else
|
||||
{
|
||||
//lazy...
|
||||
memcpy(f->buffer, cdata, f->flength);
|
||||
}
|
||||
free(cdata);
|
||||
}
|
||||
memcpy((char*)buffer+bytesread, f->buffer + f->foffset, bytestoread);
|
||||
f->foffset += bytestoread;
|
||||
bytesread += bytestoread;
|
||||
}
|
||||
else
|
||||
{
|
||||
//sectors are weird.
|
||||
//sectors are allocated for decompressed size, not compressed. I have no idea how this works.
|
||||
//time to find out.
|
||||
for (;;)
|
||||
{
|
||||
int numsects = (f->flength + (f->archive->sectorsize) - 1) / f->archive->sectorsize;
|
||||
int sectidx = f->foffset / f->archive->sectorsize;
|
||||
qboolean lastsect = false;
|
||||
int chunkofs, chunklen;
|
||||
if (sectidx >= numsects-1)
|
||||
{
|
||||
lastsect = true;
|
||||
sectidx = numsects-1;
|
||||
}
|
||||
|
||||
if (sectidx != f->buffersect || !f->buffer)
|
||||
{
|
||||
int rawsize;
|
||||
char *cdata;
|
||||
f->buffersect = sectidx;
|
||||
if (!f->sectortab)
|
||||
{
|
||||
f->sectortab = malloc((numsects+1) * sizeof(*f->sectortab));
|
||||
if (!f->sectortab)
|
||||
pSys_Error("out of memory");
|
||||
VFS_SEEK(f->archive->file, f->archiveoffset);
|
||||
VFS_READ(f->archive->file, f->sectortab, (numsects+1) * sizeof(*f->sectortab));
|
||||
|
||||
if (f->flags & MPQFileEncrypted)
|
||||
mpq_decrypt(f->sectortab, (numsects+1) * sizeof(*f->sectortab), f->encryptkey-1, true);
|
||||
}
|
||||
//data is packed, sector table gives offsets. there's an extra index on the end which is the size of the last sector.
|
||||
rawsize = f->sectortab[sectidx+1]-f->sectortab[sectidx];
|
||||
|
||||
cdata = malloc(rawsize);
|
||||
if (!cdata)
|
||||
pSys_Error("out of memory");
|
||||
if (!f->buffer)
|
||||
f->buffer = malloc(f->archive->sectorsize);
|
||||
if (!f->buffer)
|
||||
pSys_Error("out of memory");
|
||||
VFS_SEEK(f->archive->file, f->archiveoffset + f->sectortab[sectidx]);
|
||||
VFS_READ(f->archive->file, cdata, rawsize);
|
||||
|
||||
if (lastsect)
|
||||
f->bufferlength = f->flength - ((numsects-1)*f->archive->sectorsize);
|
||||
else
|
||||
f->bufferlength = f->archive->sectorsize;
|
||||
|
||||
if (f->flags & MPQFileEncrypted)
|
||||
mpq_decrypt(cdata, rawsize, f->encryptkey+sectidx, false);
|
||||
if (f->flags & (MPQFileCompressed|MPQFileDiabloCompressed))
|
||||
{
|
||||
//decompress
|
||||
MPQF_decompress(!!(f->flags&MPQFileDiabloCompressed), f->buffer, f->bufferlength, cdata, rawsize);
|
||||
}
|
||||
else
|
||||
{
|
||||
//lazy...
|
||||
memcpy(f->buffer, cdata, f->bufferlength);
|
||||
}
|
||||
free(cdata);
|
||||
}
|
||||
|
||||
chunkofs = (f->foffset%f->archive->sectorsize);
|
||||
chunklen = f->archive->sectorsize - chunkofs;
|
||||
if (chunklen > bytestoread)
|
||||
chunklen = bytestoread;
|
||||
bytestoread -= chunklen;
|
||||
memcpy((char*)buffer+bytesread, f->buffer + chunkofs, chunklen);
|
||||
f->foffset += chunklen;
|
||||
bytesread += chunklen;
|
||||
if (!chunklen || !bytestoread)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bytesread;
|
||||
}
|
||||
int MPQF_writebytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
|
||||
{
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
return 0;
|
||||
}
|
||||
qboolean MPQF_seek (struct vfsfile_s *file, unsigned long pos)
|
||||
{
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
if (pos > f->flength)
|
||||
return false;
|
||||
f->foffset = pos;
|
||||
return true;
|
||||
}
|
||||
unsigned long MPQF_tell (struct vfsfile_s *file)
|
||||
{
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
return f->foffset;
|
||||
}
|
||||
unsigned long MPQF_getlen (struct vfsfile_s *file)
|
||||
{
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
return f->flength;
|
||||
}
|
||||
void MPQF_close (struct vfsfile_s *file)
|
||||
{
|
||||
mpqfile_t *f = (mpqfile_t *)file;
|
||||
if (f->buffer)
|
||||
free(f->buffer);
|
||||
if (f->sectortab)
|
||||
free(f->sectortab);
|
||||
free(f);
|
||||
}
|
||||
void MPQF_flush (struct vfsfile_s *file)
|
||||
{
|
||||
}
|
||||
|
||||
qboolean MPQF_GetKey(unsigned int flags, unsigned int blockoffset, unsigned int blocksize, unsigned int *key)
|
||||
{
|
||||
if (flags & MPQFileEncrypted)
|
||||
{
|
||||
*key = mpq_hash_cstring("(listfile)", HASH_KEY);
|
||||
|
||||
if (flags & MPQFileOffsetAdjustedKey)
|
||||
*key = (*key + (unsigned int)(blockoffset)) ^ blocksize;
|
||||
}
|
||||
else
|
||||
*key = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
vfsfile_t *MPQ_OpenVFS(void *handle, flocation_t *loc, const char *mode)
|
||||
{
|
||||
mpqarchive_t *mpq = handle;
|
||||
mpqblock_t *block = &mpq->blockdata[loc->index];
|
||||
mpqfile_t *f;
|
||||
|
||||
if (block->flags & MPQFilePatch)
|
||||
{
|
||||
Con_Printf("Cannot cope with patch files\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = malloc(sizeof(*f));
|
||||
f->buffer = NULL;
|
||||
f->sectortab = NULL;
|
||||
f->foffset = 0;
|
||||
f->archiveoffset = block->offset;
|
||||
MPQF_GetKey(block->flags, f->archiveoffset, block->size, &f->encryptkey);
|
||||
f->flags = block->flags;
|
||||
f->archive = mpq;
|
||||
f->flength = block->size;
|
||||
f->alength = block->archived_size;
|
||||
f->funcs.ReadBytes = MPQF_readbytes;
|
||||
f->funcs.WriteBytes = MPQF_writebytes;
|
||||
f->funcs.Seek = MPQF_seek;
|
||||
f->funcs.Tell = MPQF_tell;
|
||||
f->funcs.GetLen = MPQF_getlen;
|
||||
f->funcs.Close = MPQF_close;
|
||||
f->funcs.Flush = MPQF_flush;
|
||||
|
||||
return &f->funcs;
|
||||
}
|
||||
|
||||
qboolean MPQ_PollChanges(void *handle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
searchpathfuncs_t funcs =
|
||||
{
|
||||
MPQ_GetDisplayPath,
|
||||
MPQ_ClosePath,
|
||||
MPQ_BuildHash,
|
||||
MPQ_FindFile,
|
||||
MPQ_ReadFile,
|
||||
MPQ_EnumerateFiles,
|
||||
MPQ_OpenNew,
|
||||
MPQ_GeneratePureCRC,
|
||||
MPQ_OpenVFS,
|
||||
MPQ_PollChanges,
|
||||
};
|
||||
|
||||
qintptr_t Plug_Init(qintptr_t *args)
|
||||
{
|
||||
mpq_init_cryptography();
|
||||
|
||||
//we can't cope with being closed randomly. files cannot be orphaned safely.
|
||||
pPlug_ExportNative("UnsafeClose", NULL);
|
||||
|
||||
if (!pPlug_ExportNative("FS_RegisterArchiveType_mpq", &funcs))
|
||||
{
|
||||
Con_Printf("avplug: Engine doesn't support media decoder plugins\n");
|
||||
return false;
|
||||
}
|
||||
if (!pPlug_ExportNative("FS_RegisterArchiveType_MPQ", &funcs))
|
||||
{
|
||||
Con_Printf("avplug: Engine doesn't support media decoder plugins\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
//it's this one or the engine...
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
export_t func;
|
||||
} exports_t;
|
||||
|
@ -196,6 +197,9 @@ BUILTINR(int, FS_Write, (qhandle_t handle, void *data, int len));
|
|||
#define ARGNAMES ,handle,data,len
|
||||
BUILTINR(int, FS_Read, (qhandle_t handle, void *data, int len));
|
||||
#undef ARGNAMES
|
||||
#define ARGNAMES ,handle,offsetlow,offsethigh
|
||||
BUILTINR(int, FS_Seek, (qhandle_t handle, unsigned int offsetlow, unsigned int offsethigh));
|
||||
#undef ARGNAMES
|
||||
|
||||
|
||||
#define ARGNAMES ,ip,port
|
||||
|
@ -263,21 +267,21 @@ void Con_Printf(const char *format, ...)
|
|||
vsnprintf (string, sizeof(string), format,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Con_Print(string);
|
||||
pCon_Print(string);
|
||||
}
|
||||
void Con_DPrintf(const char *format, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
static char string[1024];
|
||||
|
||||
if (!Cvar_GetFloat("developer"))
|
||||
if (!pCvar_GetFloat("developer"))
|
||||
return;
|
||||
|
||||
va_start (argptr, format);
|
||||
vsnprintf (string, sizeof(string), format,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Con_Print(string);
|
||||
pCon_Print(string);
|
||||
}
|
||||
void Sys_Errorf(const char *format, ...)
|
||||
{
|
||||
|
@ -288,12 +292,12 @@ void Sys_Errorf(const char *format, ...)
|
|||
vsnprintf (string, sizeof(string), format,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Sys_Error(string);
|
||||
pSys_Error(string);
|
||||
}
|
||||
|
||||
void BadBuiltin(void)
|
||||
{
|
||||
Sys_Error("Plugin tried calling a missing builtin\n");
|
||||
pSys_Error("Plugin tried calling a missing builtin\n");
|
||||
}
|
||||
|
||||
void Plug_InitStandardBuiltins(void)
|
||||
|
@ -422,10 +426,10 @@ qboolean Plug_Export(const char *name, export_t func)
|
|||
{
|
||||
exports[i].name = name;
|
||||
exports[i].func = func;
|
||||
return Plug_ExportToEngine(name, i);
|
||||
return pPlug_ExportToEngine(name, i);
|
||||
}
|
||||
}
|
||||
Sys_Error("Plugin exports too many functions");
|
||||
pSys_Error("Plugin exports too many functions");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#ifndef __PLUGIN_H__
|
||||
#define __PLUGIN_H__
|
||||
|
||||
#ifdef FTEPLUGIN
|
||||
#include "quakedef.h"
|
||||
#undef snprintf
|
||||
#undef vsnprintf
|
||||
#endif
|
||||
|
||||
#ifdef Q3_VM
|
||||
|
||||
typedef int qintptr_t;
|
||||
|
@ -55,8 +61,9 @@ void BadBuiltin(void);
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include "math.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifndef _VM_H
|
||||
#ifdef _WIN64
|
||||
typedef long long qintptr_t;
|
||||
typedef unsigned long long quintptr_t;
|
||||
|
@ -64,38 +71,55 @@ typedef unsigned long long quintptr_t;
|
|||
typedef long qintptr_t;
|
||||
typedef unsigned long quintptr_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#define NATIVEEXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef FTEPLUGIN
|
||||
#define pPlug_GetEngineFunction Plug_GetEngineFunction
|
||||
#define pCon_Print Con_Print
|
||||
#define pCvar_GetFloat Cvar_GetFloat
|
||||
#define pSys_Error Sys_Error
|
||||
#define pPlug_ExportToEngine Plug_ExportToEngine
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//DLLs need a wrapper to add the extra parameter and call a boring function.
|
||||
#define EBUILTIN(t, n, args) extern qintptr_t BUILTIN_##n; t n args
|
||||
#define TEST
|
||||
#ifdef TEST
|
||||
#ifdef FTEPLUGIN
|
||||
#define EBUILTIN(t, n, args) extern qintptr_t BUILTIN_##n; t p##n args
|
||||
#define BUILTINR(t, n, args) qintptr_t BUILTIN_##n; t p##n args {qintptr_t res; if (!BUILTINISVALID(n))pSys_Error("Builtin "#n" is not valid\n");res = plugin_syscall(BUILTIN_##n ARGNAMES); return *(t*)&res;}
|
||||
#define BUILTIN(t, n, args) qintptr_t BUILTIN_##n; t p##n args {if (!BUILTINISVALID(n))pSys_Error("Builtin "#n" is not valid\n");plugin_syscall(BUILTIN_##n ARGNAMES);}
|
||||
#elif defined(TEST)
|
||||
#define EBUILTIN(t, n, args) extern qintptr_t BUILTIN_##n; t n args
|
||||
#define BUILTINR(t, n, args) qintptr_t BUILTIN_##n; t n args {qintptr_t res; if (!BUILTINISVALID(n))Sys_Error("Builtin "#n" is not valid\n");res = plugin_syscall(BUILTIN_##n ARGNAMES); return *(t*)&res;}
|
||||
#define BUILTIN(t, n, args) qintptr_t BUILTIN_##n; t n args {if (!BUILTINISVALID(n))Sys_Error("Builtin "#n" is not valid\n");plugin_syscall(BUILTIN_##n ARGNAMES);}
|
||||
#else
|
||||
#define EBUILTIN(t, n, args) extern qintptr_t BUILTIN_##n; t n args
|
||||
#define BUILTINR(t, n, args) qintptr_t BUILTIN_##n; t n args {qintptr_t res = plugin_syscall(BUILTIN_##n ARGNAMES); return *(t*)&res;}
|
||||
#define BUILTIN(t, n, args) qintptr_t BUILTIN_##n; t n args {plugin_syscall(BUILTIN_##n ARGNAMES);}
|
||||
#endif
|
||||
#define CHECKBUILTIN(n) BUILTIN_##n = (qintptr_t)Plug_GetEngineFunction(#n);
|
||||
#define CHECKBUILTIN(n) BUILTIN_##n = (qintptr_t)pPlug_GetEngineFunction(#n);
|
||||
#define BUILTINISVALID(n) (BUILTIN_##n != 0)
|
||||
#ifndef QDECL
|
||||
#ifdef _WIN32
|
||||
#define QDECL __cdecl
|
||||
#else
|
||||
#define QDECL
|
||||
#endif
|
||||
#endif
|
||||
extern qintptr_t (*plugin_syscall)( qintptr_t arg, ... );
|
||||
|
||||
#ifdef _WIN32
|
||||
void strlcpy(char *d, const char *s, int n);
|
||||
int snprintf(char *buffer, size_t maxlen, const char *format, ...);
|
||||
//int snprintf(char *buffer, size_t maxlen, const char *format, ...);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -104,6 +128,8 @@ int snprintf(char *buffer, size_t maxlen, const char *format, ...);
|
|||
#define NATIVEEXPORT
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef FTEPLUGIN
|
||||
#ifdef __cplusplus
|
||||
typedef enum {qfalse, qtrue} qboolean;
|
||||
#else
|
||||
|
@ -111,8 +137,10 @@ typedef enum {qfalse, qtrue} qboolean;
|
|||
#define false qfalse
|
||||
#define true qtrue
|
||||
#endif
|
||||
typedef void *qhandle_t;
|
||||
typedef float vec3_t[3];
|
||||
typedef unsigned char qbyte;
|
||||
#endif
|
||||
typedef void *qhandle_t;
|
||||
typedef void* funcptr_t;
|
||||
|
||||
|
||||
|
@ -191,6 +219,8 @@ EBUILTIN(void, Draw_Colour3f, (float r, float g, float b));
|
|||
EBUILTIN(void, Draw_Colour4f, (float r, float g, float b, float a));
|
||||
EBUILTIN(void, SCR_CenterPrint, (char *s));
|
||||
|
||||
EBUILTIN(void, S_RawAudio, (int sourceid, void *data, int speed, int samples, int channels, int width));
|
||||
|
||||
EBUILTIN(int, ReadInputBuffer, (void *inputbuffer, int buffersize));
|
||||
EBUILTIN(int, UpdateInputBuffer, (void *inputbuffer, int bytes));
|
||||
|
||||
|
@ -198,6 +228,7 @@ EBUILTIN(int, FS_Open, (char *name, qhandle_t *handle, int mode));
|
|||
EBUILTIN(void, FS_Close, (qhandle_t handle));
|
||||
EBUILTIN(int, FS_Write, (qhandle_t handle, void *data, int len));
|
||||
EBUILTIN(int, FS_Read, (qhandle_t handle, void *data, int len));
|
||||
EBUILTIN(int, FS_Seek, (qhandle_t handle, unsigned int offsetlow, unsigned int offsethigh));
|
||||
|
||||
EBUILTIN(qhandle_t, Net_TCPConnect, (char *ip, int port));
|
||||
EBUILTIN(qhandle_t, Net_TCPListen, (char *ip, int port, int maxcount));
|
||||
|
@ -211,7 +242,7 @@ EBUILTIN(void, Net_Close, (qhandle_t socket));
|
|||
|
||||
|
||||
#if defined(_WIN32) || defined(Q3_VM)
|
||||
int vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs);
|
||||
//int vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs);
|
||||
#endif
|
||||
|
||||
#ifdef Q3_VM
|
||||
|
@ -230,7 +261,6 @@ qboolean Plug_Export(const char *name, export_t func);
|
|||
void Con_Printf(const char *format, ...);
|
||||
void Con_DPrintf(const char *format, ...); //not a particuarly efficient implementation, so beware.
|
||||
void Sys_Errorf(const char *format, ...);
|
||||
typedef unsigned char qbyte;
|
||||
void Q_strncpyz(char *d, const char *s, int n);
|
||||
|
||||
|
||||
|
@ -251,7 +281,7 @@ void Q_strncpyz(char *d, const char *s, int n);
|
|||
//
|
||||
// qvm_api.c
|
||||
//
|
||||
int vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs);
|
||||
//int vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs);
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
|
@ -267,7 +297,11 @@ typedef struct {
|
|||
int width;
|
||||
int height;
|
||||
} vmvideo_t;
|
||||
#ifdef _VM_H
|
||||
#define vid ohnoes
|
||||
#else
|
||||
extern vmvideo_t vid;
|
||||
#endif
|
||||
|
||||
#define VMCvar_SetString(c,v) \
|
||||
do{ \
|
||||
|
@ -283,13 +317,15 @@ extern vmvideo_t vid;
|
|||
} while(0) \
|
||||
|
||||
|
||||
#ifndef MAX_INFO_KEY
|
||||
#define MAX_INFO_KEY 64
|
||||
char *Info_ValueForKey (char *s, char *key);
|
||||
void Info_RemoveKey (char *s, char *key);
|
||||
#endif
|
||||
char *Info_ValueForKey (char *s, const char *key);
|
||||
void Info_RemoveKey (char *s, const char *key);
|
||||
void Info_RemovePrefixedKeys (char *start, char prefix);
|
||||
void Info_RemoveNonStarKeys (char *start);
|
||||
void Info_SetValueForKey (char *s, char *key, char *value, int maxsize);
|
||||
void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize);
|
||||
void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsize);
|
||||
void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ int vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs)
|
|||
int tokens=0;
|
||||
char *string;
|
||||
char tempbuffer[64];
|
||||
unsigned int _uint;
|
||||
int _int;
|
||||
float _float;
|
||||
int i;
|
||||
|
@ -93,22 +94,15 @@ retry:
|
|||
tokens++;
|
||||
break;
|
||||
case 'x':
|
||||
_int = va_arg(vargs, int);
|
||||
if (_int < 0)
|
||||
{
|
||||
if (maxlen-- == 0)
|
||||
{*buffer++='\0';return tokens;}
|
||||
*buffer++ = '-';
|
||||
_int *= -1;
|
||||
}
|
||||
_uint = va_arg(vargs, unsigned int);
|
||||
i = sizeof(tempbuffer)-2;
|
||||
tempbuffer[sizeof(tempbuffer)-1] = '\0';
|
||||
while(_int)
|
||||
tempbuffer[i+1] = '\0';
|
||||
while(_uint)
|
||||
{
|
||||
tempbuffer[i] = _int%16 + '0';
|
||||
tempbuffer[i] = (_uint&0xf) + '0';
|
||||
if (tempbuffer[i] > '9')
|
||||
tempbuffer[i] = tempbuffer[i] - ':' + 'a';
|
||||
_int/=16;
|
||||
_uint/=16;
|
||||
i--;
|
||||
}
|
||||
string = tempbuffer+i+1;
|
||||
|
@ -553,7 +547,7 @@ int rand(void)
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32_VERSION) || defined(__MINGW__) || defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#if 0//defined(__MINGW32_VERSION) || defined(__MINGW__) || defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#else
|
||||
void strlcpy(char *d, const char *s, int n)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue