284 lines
8.1 KiB
NASM
284 lines
8.1 KiB
NASM
;
|
||
; Command & Conquer Red Alert(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/>.
|
||
;
|
||
|
||
; $Header: F:\projects\c&c0\vcs\code\lcwcomp.asv 5.0 11 Nov 1996 09:40:34 JOE_BOSTIC $
|
||
;***************************************************************************
|
||
;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
|
||
;***************************************************************************
|
||
;* *
|
||
;* Project Name : Library routine *
|
||
;* *
|
||
;* File Name : COMPRESS.ASM *
|
||
;* *
|
||
;* Programmer : Louis Castle *
|
||
;* *
|
||
;* Last Update : 20 August, 1990 [CY] *
|
||
;* *
|
||
;*-------------------------------------------------------------------------*
|
||
;* Functions: *
|
||
;* *
|
||
;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
|
||
|
||
IDEAL
|
||
P386
|
||
MODEL USE32 FLAT
|
||
|
||
GLOBAL C LCW_Comp :NEAR
|
||
|
||
CODESEG
|
||
|
||
; ----------------------------------------------------------------
|
||
;
|
||
; Here are prototypes for the routines defined within this module:
|
||
;
|
||
; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length);
|
||
;
|
||
; ----------------------------------------------------------------
|
||
|
||
|
||
|
||
;***********************************************************
|
||
;
|
||
; ULONG LCW_Compress(BYTE *source, BYTE *dest, ULONG length)
|
||
;
|
||
; returns the size of the compressed data in bytes
|
||
;
|
||
;*
|
||
PROC LCW_Comp C near
|
||
USES ebx,ecx,edx,edi,esi
|
||
|
||
ARG source:DWORD
|
||
ARG dest:DWORD
|
||
ARG datasize:DWORD
|
||
|
||
LOCAL inlen:DWORD
|
||
LOCAL a1stdest:DWORD
|
||
LOCAL a1stsrc:DWORD
|
||
LOCAL lenoff:DWORD
|
||
LOCAL ndest:DWORD
|
||
LOCAL count:DWORD
|
||
LOCAL matchoff:DWORD
|
||
LOCAL end_of_data:DWORD
|
||
|
||
|
||
cld
|
||
mov edi,[dest]
|
||
mov esi,[source]
|
||
mov edx,[datasize] ; get length of data to compress
|
||
|
||
; mov ax,ds
|
||
; mov es,ax
|
||
|
||
;
|
||
; 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
|
||
;
|
||
cld ; make sure all string commands are forward
|
||
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
|
||
??loop:
|
||
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:
|
||
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 [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:
|
||
mov edi,ebx
|
||
??notrunlength:
|
||
|
||
??oploop:
|
||
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
|
||
|
||
; start of change MH 9-24-91
|
||
jne short ??notend ; if found mismatch then di - bx = match count
|
||
|
||
inc edi ; else cx = 0 and di + 1 - bx = match count
|
||
|
||
??notend:
|
||
; end of change MH 9-24-91
|
||
|
||
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:
|
||
|
||
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:
|
||
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:
|
||
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:
|
||
cmp [DWORD PTR inlen],0 ; is it doing a length?
|
||
jnz short ??len ; if so, skip code
|
||
|
||
??lenin1:
|
||
mov [lenoff],edi ; save the length code offset
|
||
mov al,80h ; set the length to 0
|
||
stosb ; save it
|
||
|
||
??len:
|
||
mov ebx,[lenoff] ; get the offset of the length code
|
||
cmp [BYTE PTR ebx],0BFh ; see if its maxed out
|
||
je ??lenin1 ; if so put out a new len code
|
||
|
||
??stolen:
|
||
inc [BYTE PTR ebx] ; inc the count code
|
||
lodsb ; get the byte
|
||
stosb ; store it
|
||
mov [DWORD PTR inlen],1 ; we are now in a length so save it
|
||
jmp short ??nxt ; do the next code
|
||
|
||
??longrun:
|
||
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:
|
||
mov eax,[matchoff] ; get the offset
|
||
sub eax,[a1stsrc] ; make it relative tot he start of data
|
||
??srunnxt:
|
||
stosw ; store it
|
||
; this code common to all runs
|
||
add esi,[count] ; add in the length of the run to the source
|
||
mov [DWORD PTR inlen],0 ; set the in leght flag to false
|
||
|
||
;=======================================================================
|
||
|
||
??nxt:
|
||
cmp esi,[end_of_data] ; see if we did the whole pic
|
||
jae short ??out ; if so, cool! were done
|
||
|
||
jmp ??loop
|
||
|
||
??out:
|
||
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
|
||
|
||
|
||
ret
|
||
|
||
ENDP LCW_Comp
|
||
|
||
|
||
END
|
||
|