CnC_Remastered_Collection/REDALERT/LCWCOMP.ASM
PG-SteveT 03416d24e1 Initial Source Code commit
Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered.
2020-05-27 12:16:20 -07:00

287 lines
8.3 KiB
NASM

;
; Copyright 2020 Electronic Arts Inc.
;
; TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
; in the hope that it will be useful, but with permitted additional restrictions
; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
; distributed with this program. You should have received a copy of the
; GNU General Public License along with permitted additional restrictions
; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>.
; $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
.MODEL FLAT
;GLOBAL C LCW_Comp :NEAR
externdef C LCW_Comp:near
;CODESEG
.code
; ----------------------------------------------------------------
;
; 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
;
;*
LCW_Comp proc C source:DWORD, dest:DWORD, datasize:DWORD
;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
pushad
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
popad
ret
LCW_Comp endp
END