444 lines
16 KiB
C++
444 lines
16 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : Command & Conquer *
|
|
* *
|
|
* $Archive:: /G/wwlib/lcw.cpp $*
|
|
* *
|
|
* $Author:: Neal_k $*
|
|
* *
|
|
* $Modtime:: 10/04/99 10:25a $*
|
|
* *
|
|
* $Revision:: 4 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* LCW_Comp -- Performes LCW compression on a block of data. *
|
|
* LCW_Uncomp -- Decompress an LCW encoded data block. *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "always.h"
|
|
#include "lcw.h"
|
|
|
|
/***************************************************************************
|
|
* LCW_Uncomp -- Decompress an LCW encoded data block. *
|
|
* *
|
|
* Uncompress data to the following codes in the format b = byte, w = word *
|
|
* n = byte code pulled from compressed data. *
|
|
* *
|
|
* Command code, n |Description *
|
|
* ------------------------------------------------------------------------*
|
|
* n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest *
|
|
* n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source*
|
|
* n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 *
|
|
* n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 *
|
|
* n=11111110,w1,b1 |long run of byte b1 for w1 bytes *
|
|
* n=10000000 |end of data reached *
|
|
* *
|
|
* *
|
|
* INPUT: *
|
|
* void * source ptr *
|
|
* void * destination ptr *
|
|
* unsigned long length of uncompressed data *
|
|
* *
|
|
* *
|
|
* OUTPUT: *
|
|
* unsigned long # of destination bytes written *
|
|
* *
|
|
* WARNINGS: *
|
|
* 3rd argument is dummy. It exists to provide cross-platform *
|
|
* compatibility. Note therefore that this implementation does not *
|
|
* check for corrupt source data by testing the uncompressed length. *
|
|
* *
|
|
* HISTORY: *
|
|
* 03/20/1995 IML : Created. *
|
|
*=========================================================================*/
|
|
int LCW_Uncomp(void const * source, void * dest, unsigned long )
|
|
{
|
|
unsigned char * source_ptr, * dest_ptr, * copy_ptr;
|
|
unsigned char op_code, data;
|
|
unsigned count;
|
|
unsigned * word_dest_ptr;
|
|
unsigned word_data;
|
|
|
|
/* Copy the source and destination ptrs. */
|
|
source_ptr = (unsigned char*) source;
|
|
dest_ptr = (unsigned char*) dest;
|
|
|
|
for (;;) {
|
|
|
|
/* Read in the operation code. */
|
|
op_code = *source_ptr++;
|
|
|
|
if (!(op_code & 0x80)) {
|
|
|
|
/* Do a short copy from destination. */
|
|
count = (op_code >> 4) + 3;
|
|
copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8));
|
|
|
|
while (count--) *dest_ptr++ = *copy_ptr++;
|
|
|
|
} else {
|
|
|
|
if (!(op_code & 0x40)) {
|
|
|
|
if (op_code == 0x80) {
|
|
|
|
/* Return # of destination bytes written. */
|
|
return ((unsigned long) (dest_ptr - (unsigned char*) dest));
|
|
|
|
} else {
|
|
|
|
/* Do a medium copy from source. */
|
|
count = op_code & 0x3f;
|
|
|
|
while (count--) *dest_ptr++ = *source_ptr++;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (op_code == 0xfe) {
|
|
|
|
/* Do a long run. */
|
|
count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
|
|
word_data = data = *(source_ptr + 2);
|
|
word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data;
|
|
source_ptr += 3;
|
|
|
|
copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3);
|
|
count -= (copy_ptr - dest_ptr);
|
|
while (dest_ptr < copy_ptr) *dest_ptr++ = data;
|
|
|
|
word_dest_ptr = (unsigned*) dest_ptr;
|
|
|
|
dest_ptr += (count & 0xfffffffc);
|
|
|
|
while (word_dest_ptr < (unsigned*) dest_ptr) {
|
|
*word_dest_ptr = word_data;
|
|
*(word_dest_ptr + 1) = word_data;
|
|
word_dest_ptr += 2;
|
|
}
|
|
|
|
copy_ptr = dest_ptr + (count & 0x3);
|
|
while (dest_ptr < copy_ptr) *dest_ptr++ = data;
|
|
|
|
} else {
|
|
|
|
if (op_code == 0xff) {
|
|
|
|
/* Do a long copy from destination. */
|
|
count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
|
|
copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8);
|
|
source_ptr += 4;
|
|
|
|
while (count--) *dest_ptr++ = *copy_ptr++;
|
|
|
|
} else {
|
|
|
|
/* Do a medium copy from destination. */
|
|
count = (op_code & 0x3f) + 3;
|
|
copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
|
|
source_ptr += 2;
|
|
|
|
while (count--) *dest_ptr++ = *copy_ptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LCW_Comp -- Performes LCW compression on a block of data. *
|
|
* *
|
|
* This routine will compress a block of data using the LCW compression method. LCW has *
|
|
* the primary characteristic of very fast uncompression at the expense of very slow *
|
|
* compression times. *
|
|
* *
|
|
* INPUT: source -- Pointer to the source data to compress. *
|
|
* *
|
|
* dest -- Pointer to the destination location to store the compressed data *
|
|
* to. *
|
|
* *
|
|
* datasize -- The size (in bytes) of the source data to compress. *
|
|
* *
|
|
* OUTPUT: Returns with the number of bytes of output data stored into the destination *
|
|
* buffer. *
|
|
* *
|
|
* WARNINGS: Be sure that the destination buffer is big enough. The maximum size required *
|
|
* for the destination buffer is (datasize + datasize/128). *
|
|
* *
|
|
* HISTORY: *
|
|
* 05/20/1997 JLB : Created. *
|
|
*=============================================================================================*/
|
|
/*ARGSUSED*/
|
|
int LCW_Comp(void const * source, void * dest, int datasize)
|
|
{
|
|
int retval = 0;
|
|
#ifdef _WINDOWS
|
|
long inlen = 0;
|
|
long a1stdest = 0;
|
|
long a1stsrc = 0;
|
|
long lenoff = 0;
|
|
long ndest = 0;
|
|
long count = 0;
|
|
long matchoff = 0;
|
|
long end_of_data =0;
|
|
#ifdef _DEBUG
|
|
inlen = inlen;
|
|
a1stdest = a1stdest;
|
|
a1stsrc = a1stsrc;
|
|
lenoff = lenoff;
|
|
ndest = ndest;
|
|
count = count;
|
|
matchoff = matchoff;
|
|
end_of_data = end_of_data;
|
|
#endif
|
|
|
|
__asm {
|
|
cld // make sure all string commands are forward
|
|
mov edi,[dest]
|
|
mov esi,[source]
|
|
mov edx,[datasize] // get length of data to compress
|
|
|
|
// compress data to the following codes in the format b = byte, w = word
|
|
// n = byte code pulled from compressed data
|
|
// Bit field of n command description
|
|
// n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3
|
|
// n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes
|
|
// n=11xxxxxx,w1 med run run x+3 bytes from offset w1
|
|
// n=11111111,w1,w2 long run run w1 bytes from offset w2
|
|
// n=10000000 end end of data reached
|
|
|
|
mov ebx,esi
|
|
add ebx,edx
|
|
mov [end_of_data],ebx
|
|
mov [inlen],1 //; set the in-length flag
|
|
mov [a1stdest],edi //; save original dest offset for size calc
|
|
mov [a1stsrc],esi //; save offset of first byte of data
|
|
mov [lenoff],edi //; save the offset of the legth of this len
|
|
sub eax,eax
|
|
mov al,081h //; the first byte is always a len
|
|
stosb //; write out a len of 1
|
|
lodsb //; get the byte
|
|
stosb //; save it
|
|
}
|
|
|
|
loopstart:
|
|
__asm {
|
|
mov [ndest],edi //; save offset of compressed data
|
|
mov edi,[a1stsrc] //; get the offset to the first byte of data
|
|
mov [count],1 //; set the count of run to 0
|
|
}
|
|
searchloop:
|
|
__asm {
|
|
sub eax,eax
|
|
mov al,[esi] //; get the current byte of data
|
|
cmp al,[esi+64]
|
|
jne short notrunlength
|
|
|
|
mov ebx,edi
|
|
|
|
mov edi,esi
|
|
mov ecx,[end_of_data]
|
|
sub ecx,edi
|
|
repe scasb
|
|
dec edi
|
|
mov ecx,edi
|
|
sub ecx,esi
|
|
cmp ecx,65
|
|
jb short notlongenough
|
|
|
|
mov [inlen],0 //; clear the in-length flag
|
|
// mov [DWORD PTR inlen],0 //; clear the in-length flag
|
|
mov esi,edi
|
|
mov edi,[ndest] //; get the offset of our compressed data
|
|
|
|
mov ah,al
|
|
mov al,0FEh
|
|
stosb
|
|
xchg ecx,eax
|
|
stosw
|
|
mov al,ch
|
|
stosb
|
|
|
|
mov [ndest],edi //; save offset of compressed data
|
|
mov edi,ebx
|
|
jmp searchloop
|
|
}
|
|
notlongenough:
|
|
__asm {
|
|
mov edi,ebx
|
|
}
|
|
notrunlength:
|
|
oploop:
|
|
__asm {
|
|
mov ecx,esi //; get the address of the last byte +1
|
|
sub ecx,edi //; get the total number of bytes left to comp
|
|
jz short searchdone
|
|
|
|
repne scasb //; look for a match
|
|
jne short searchdone //; if we don't find one we're done
|
|
|
|
mov ebx,[count]
|
|
mov ah,[esi+ebx-1]
|
|
cmp ah,[edi+ebx-2]
|
|
|
|
jne oploop
|
|
|
|
mov edx,esi //; save this spot for the next search
|
|
mov ebx,edi //; save this spot for the length calc
|
|
dec edi //; back up one for compare
|
|
mov ecx,[end_of_data] //; get the end of data
|
|
sub ecx,esi //; sub current source for max len
|
|
|
|
repe cmpsb //; see how many bytes match
|
|
|
|
jne short notend //; if found mismatch then di - bx = match count
|
|
|
|
inc edi //; else cx = 0 and di + 1 - bx = match count
|
|
}
|
|
notend:
|
|
__asm {
|
|
mov esi,edx //; restore si
|
|
mov eax,edi //; get the dest
|
|
sub eax,ebx //; sub the start for total bytes that match
|
|
mov edi,ebx //; restore dest
|
|
cmp eax,[count] //; see if its better than before
|
|
jb searchloop //; if not keep looking
|
|
|
|
mov [count],eax //; if so keep the count
|
|
dec ebx //; back it up for the actual match offset
|
|
mov [matchoff],ebx //; save the offset for later
|
|
jmp searchloop //; loop until we searched it all
|
|
}
|
|
searchdone:
|
|
__asm {
|
|
mov ecx,[count] //; get the count of the longest run
|
|
mov edi,[ndest] //; get the offset of our compressed data
|
|
cmp ecx,2 //; see if its not enough run to matter
|
|
jbe short lenin //; if its 0,1, or 2 its too small
|
|
|
|
cmp ecx,10 //; if not, see if it would fit in a short
|
|
ja short medrun //; if not, see if its a medium run
|
|
|
|
mov eax,esi //; if its short get the current address
|
|
sub eax,[matchoff] //; sub the offset of the match
|
|
cmp eax,0FFFh //; if its less than 12 bits its a short
|
|
ja short medrun //; if its not, its a medium
|
|
}
|
|
//shortrun:
|
|
__asm {
|
|
sub ebx,ebx
|
|
mov bl,cl //; get the length (3-10)
|
|
sub bl,3 //; sub 3 for a 3 bit number 0-7
|
|
shl bl,4 //; shift it left 4
|
|
add ah,bl //; add in the length for the high nibble
|
|
xchg ah,al //; reverse the bytes for a word store
|
|
jmp short srunnxt //; do the run fixup code
|
|
}
|
|
medrun:
|
|
__asm {
|
|
cmp ecx,64 //; see if its a short run
|
|
ja short longrun //; if not, oh well at least its long
|
|
|
|
sub cl,3 //; back down 3 to keep it in 6 bits
|
|
or cl,0C0h //; the highest bits are always on
|
|
mov al,cl //; put it in al for the stosb
|
|
stosb //; store it
|
|
jmp short medrunnxt //; do the run fixup code
|
|
}
|
|
lenin:
|
|
__asm {
|
|
cmp [inlen],0 //; is it doing a length?
|
|
// cmp [DWORD PTR inlen],0 //; is it doing a length?
|
|
jnz short len //; if so, skip code
|
|
}
|
|
lenin1:
|
|
__asm {
|
|
mov [lenoff],edi //; save the length code offset
|
|
mov al,80h //; set the length to 0
|
|
stosb //; save it
|
|
}
|
|
len:
|
|
__asm {
|
|
mov ebx,[lenoff] //; get the offset of the length code
|
|
cmp [ebx],0BFh //; see if its maxed out
|
|
// cmp [BYTE PTR ebx],0BFh //; see if its maxed out
|
|
je lenin1 //; if so put out a new len code
|
|
}
|
|
//stolen:
|
|
__asm {
|
|
inc [ebx] //; inc the count code
|
|
// inc [BYTE PTR ebx] //; inc the count code
|
|
lodsb //; get the byte
|
|
stosb //; store it
|
|
mov [inlen],1 //; we are now in a length so save it
|
|
// mov [DWORD PTR inlen],1 //; we are now in a length so save it
|
|
jmp short nxt //; do the next code
|
|
}
|
|
longrun:
|
|
__asm {
|
|
mov al,0ffh //; its a long so set a code of FF
|
|
stosb //; store it
|
|
|
|
mov eax,[count] //; send out the count
|
|
stosw //; store it
|
|
}
|
|
medrunnxt:
|
|
__asm {
|
|
mov eax,[matchoff] //; get the offset
|
|
sub eax,[a1stsrc] //; make it relative tot he start of data
|
|
}
|
|
srunnxt:
|
|
__asm {
|
|
stosw //; store it
|
|
//; this code common to all runs
|
|
add esi,[count] //; add in the length of the run to the source
|
|
mov [inlen],0 //; set the in leght flag to false
|
|
// mov [DWORD PTR inlen],0 //; set the in leght flag to false
|
|
}
|
|
nxt:
|
|
__asm {
|
|
cmp esi,[end_of_data] //; see if we did the whole pic
|
|
jae short outofhere //; if so, cool! were done
|
|
|
|
jmp loopstart
|
|
}
|
|
outofhere:
|
|
__asm {
|
|
mov ax,080h //; remember to send an end of data code
|
|
stosb //; store it
|
|
mov eax,edi //; get the last compressed address
|
|
sub eax,[a1stdest] //; sub the first for the compressed size
|
|
mov [retval],eax
|
|
}
|
|
#endif
|
|
return(retval);
|
|
}
|
|
#endif
|
|
|
|
|