;
; 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]>.

; **************************************************************************
; **   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 : WSA Support routines			   *
; *                                                                        *
; *                    File Name : XORDELTA.ASM                            *
; *                                                                        *
; *                   Programmer : Scott K. Bowen			   *
; *                                                                        *
; *                  Last Update :May 23, 1994   [SKB]                     *
; *                                                                        *
; *------------------------------------------------------------------------*
; * Functions:                                                             *
;*   Apply_XOR_Delta -- Apply XOR delta data to a buffer.                  *
;*   Apply_XOR_Delta_To_Page_Or_Viewport -- Calls the copy or the XOR funti*
;*   Copy_Delta_buffer -- Copies XOR Delta Data to a section of a page.    *
;*   XOR_Delta_Buffer -- Xor's the data in a XOR Delta format to a page.   *
; * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*

;IDEAL
;P386
;MODEL USE32 FLAT
.model flat



;LOCALS ??

; These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter.  If
; These change, make sure and change their values in wsa.cpp.
DO_XOR		equ	0
DO_COPY		equ	1
TO_VIEWPORT	equ	0
TO_PAGE		equ	2

;
; Routines defined in this module
;
;
; UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr);
; PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD copy)
;
;	PROC	C XOR_Delta_Buffer
;	PROC	C Copy_Delta_Buffer
;

externdef C Apply_XOR_Delta:NEAR
externdef C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR



	;CODESEG
	.code


;***************************************************************************
;* APPLY_XOR_DELTA -- Apply XOR delta data to a linear buffer.             *
;*   AN example of this in C is at the botton of the file commented out.   *
;*                                                                         *
;* INPUT:  BYTE *target - destination buffer.                              *
;*         BYTE *delta - xor data to be delta uncompress.                  *
;*                                                                         *
;* OUTPUT:                                                                 *
;*                                                                         *
;* WARNINGS:                                                               *
;*                                                                         *
;* HISTORY:                                                                *
;*   05/23/1994 SKB : Created.                                             *
;*=========================================================================*
Apply_XOR_Delta proc C public USES ebx ecx edx esi edi target:DWORD, delta:DWORD
	
	;USES 	ebx,ecx,edx,edi,esi
	;ARG	target:DWORD 		; pointers.
	;ARG	delta:DWORD		; pointers.

	; Optimized for 486/pentium by rearanging instructions.
	mov	edi,[target]		; get our pointers into offset registers.
	mov	esi,[delta]

	cld				; make sure we go forward
	xor	ecx,ecx			; use cx for loop

top_loop:
	xor	eax,eax			; clear out eax.
	mov	al,[esi]		; get delta source byte
	inc	esi

	test	al,al			; check for a SHORTDUMP ; check al incase of sign value.
	je	short_run
	js	check_others

;
; SHORTDUMP
;
	mov	ecx,eax			; stick count in cx

dump_loop:
	mov	al,[esi]		;get delta XOR byte
	xor	[edi],al		; xor that byte on the dest
	inc	esi
	inc	edi
	dec	ecx
	jnz	dump_loop
	jmp	top_loop

;
; SHORTRUN
;

short_run:
	mov	cl,[esi]		; get count
	inc	esi			; inc delta source

do_run:
	mov	al,[esi]		; get XOR byte
	inc	esi

run_loop:
	xor	[edi],al		; xor that byte.

	inc	edi			; go to next dest pixel
	dec	ecx			; one less to go.
	jnz	run_loop
	jmp	top_loop

;
; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP
;

check_others:
	sub	eax,080h		; opcode -= 0x80
	jnz	do_skip		; if zero then get next word, otherwise use remainder.

	mov	ax,[esi]
	lea	esi,[esi+2]		; get word code in ax
	test	ax,ax			; set flags. (not 32bit register so neg flag works)
	jle	not_long_skip

;
; SHORTSKIP AND LONGSKIP
;
do_skip:
	add	edi,eax			; do the skip.
	jmp	top_loop


not_long_skip:
	jz	stop			; long count of zero means stop
	sub	eax,08000h     		; opcode -= 0x8000
	test	eax,04000h		; is it a LONGRUN (code & 0x4000)?
	je	long_dump

;
; LONGRUN
;
	sub	eax,04000h		; opcode -= 0x4000
	mov	ecx,eax			; use cx as loop count
	jmp	do_run		; jump to run code.


;
; LONGDUMP
;

long_dump:
	mov	ecx,eax			; use cx as loop count
	jmp	dump_loop		; go to the dump loop.

stop:

	ret

Apply_XOR_Delta endp


;----------------------------------------------------------------------------


;***************************************************************************
;* APPLY_XOR_DELTA_To_Page_Or_Viewport -- Calls the copy or the XOR funtion.           *
;*                                                                         *
;*									   *
;* 	This funtion is call to either xor or copy XOR_Delta data onto a   *
;*	page instead of a buffer.  The routine will set up the registers   *
;*	need for the actual routines that will perform the copy or xor.	   *
;*									   *
;*	The registers are setup as follows :				   *
;*		es:edi - destination segment:offset onto page.		   *
;*		ds:esi - source buffer segment:offset of delta data.	   *
;*		dx,cx,ax - are all zeroed out before entry.		   *
;*                                                                         *
;* INPUT:                                                                  *
;*                                                                         *
;* OUTPUT:                                                                 *
;*                                                                         *
;* WARNINGS:                                                               *
;*                                                                         *
;* HISTORY:                                                                *
;*   03/09/1992  SB : Created.                                             *
;*=========================================================================*


Apply_XOR_Delta_To_Page_Or_Viewport proc C public USES ebx ecx edx esi edi target:DWORD, delta:DWORD, xwidth:DWORD, nextrow:DWORD, copy:DWORD

	;USES 	ebx,ecx,edx,edi,esi
	;ARG	target:DWORD		; pointer to the destination buffer.
	;ARG	delta:DWORD		; pointer to the delta buffer.
	;ARG	width:DWORD		; width of animation.
	;ARG	nextrow:DWORD		; Page/Buffer width - anim width.
	;ARG	copy:DWORD		; should it be copied or xor'd?


	mov	edi,[target]		; Get the target pointer.
	mov	esi,[delta]		; Get the destination pointer.

	xor	eax,eax			; clear eax, later put them into ecx and edx.

	cld				; make sure we go forward

	mov	ebx,[nextrow]		; get the amount to add to get to next row from end.  push it later...

	mov	ecx,eax			; use cx for loop
	mov	edx,eax			; use dx to count the relative column.

	push	ebx			; push nextrow onto the stack for Copy/XOR_Delta_Buffer.
	mov	ebx,[xwidth]		; bx will hold the max column for speed compares

; At this point, all the registers have been set up.  Now call the correct function
; to either copy or xor the data.

	cmp	[copy],DO_XOR		; Do we want to copy or XOR
	je	xorfunct		; Jump to XOR if not copy
	call	Copy_Delta_Buffer	; Call the function to copy the delta buffer.
	jmp	didcopy		; jump past XOR
xorfunct:
	call	XOR_Delta_Buffer	; Call funtion to XOR the deltat buffer.
didcopy:
	pop	ebx			; remove the push done to pass a value.

	ret

Apply_XOR_Delta_To_Page_Or_Viewport ENDP
;----------------------------------------------------------------------------


;***************************************************************************
;* XOR_DELTA_BUFFER -- Xor's the data in a XOR Delta format to a page.     *
;*	This will only work right if the page has the previous data on it. *
;*	This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport.   *
;*      The registers must be setup as follows :                           *
;*                                                                         *
;* INPUT:                                                                  *
;*	es:edi - destination segment:offset onto page.		 	   *
;*	ds:esi - source buffer segment:offset of delta data.	 	   *
;*	edx,ecx,eax - are all zeroed out before entry.		 	   *
;*                                                                         *
;* OUTPUT:                                                                 *
;*                                                                         *
;* WARNINGS:                                                               *
;*                                                                         *
;* HISTORY:                                                                *
;*   03/09/1992  SB : Created.                                             *
;*=========================================================================*

XOR_Delta_Buffer proc C nextrow:DWORD

top_loop2:
	xor	eax,eax			; clear out eax.
	mov	al,[esi]		; get delta source byte
	inc	esi

	test	al,al			; check for a SHORTDUMP ; check al incase of sign value.
	je	short_run2
	js	check_others2

;
; SHORTDUMP
;
	mov	ecx,eax			; stick count in cx

dump_loop2:
	mov	al,[esi]		; get delta XOR byte
	xor	[edi],al		; xor that byte on the dest
	inc	esi
	inc	edx			; increment our count on current column
	inc	edi
	cmp	edx,ebx			; are we at the final column
	jne	end_col1		; if not the jmp over the code

	sub	edi,edx			; get our column back to the beginning.
	xor	edx,edx			; zero out our column counter
	add	edi,[nextrow]		; jump to start of next row
end_col1:

	dec	ecx
	jnz	dump_loop2
	jmp	top_loop2

;
; SHORTRUN
;

short_run2:
	mov	cl,[esi]		; get count
	inc	esi			; inc delta source

do_run2:
	mov	al,[esi]		; get XOR byte
	inc	esi

run_loop2:
	xor	[edi],al		; xor that byte.

	inc	edx			; increment our count on current column
	inc	edi			; go to next dest pixel
	cmp	edx,ebx			; are we at the final column
	jne	end_col2		; if not the jmp over the code

	sub	edi,ebx			; get our column back to the beginning.
	xor	edx,edx			; zero out our column counter
	add	edi,[nextrow]		; jump to start of next row
end_col2:


	dec	ecx
	jnz	run_loop2
	jmp	top_loop2

;
; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP
;

check_others2:
	sub	eax,080h		; opcode -= 0x80
	jnz	do_skip2		; if zero then get next word, otherwise use remainder.

	mov	ax,[esi]		; get word code in ax
	lea	esi,[esi+2]
	test	ax,ax			; set flags. (not 32bit register so neg flag works)
	jle	not_long_skip2

;
; SHORTSKIP AND LONGSKIP
;
do_skip2:
	sub	edi,edx			; go back to beginning or row.
	add	edx,eax			; incriment our count on current row
recheck2:
	cmp	edx,ebx			; are we past the end of the row
	jb	end_col3  		; if not the jmp over the code

	sub	edx,ebx			; Subtract width from the col counter
	add	edi,[nextrow]  		; jump to start of next row
	jmp	recheck2		; jump up to see if we are at the right row
end_col3:
	add	edi,edx			; get to correct position in row.
	jmp	top_loop2


not_long_skip2:
	jz	stop2			; long count of zero means stop
	sub	eax,08000h     		; opcode -= 0x8000
	test	eax,04000h		; is it a LONGRUN (code & 0x4000)?
	je	long_dump2

;
; LONGRUN
;
	sub	eax,04000h		; opcode -= 0x4000
	mov	ecx,eax			; use cx as loop count
	jmp	do_run2		; jump to run code.


;
; LONGDUMP
;

long_dump2:
	mov	ecx,eax			; use cx as loop count
	jmp	dump_loop2		; go to the dump loop.

stop2:

	ret


XOR_Delta_Buffer	ENDP	

;----------------------------------------------------------------------------


;***************************************************************************
;* COPY_DELTA_BUFFER -- Copies XOR Delta Data to a section of a page.      *
;*	This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport.   *
;*      The registers must be setup as follows :                           *
;*                                                                         *
;* INPUT:                                                                  *
;*	es:edi - destination segment:offset onto page.		 	   *
;*	ds:esi - source buffer segment:offset of delta data.	 	   *
;*	dx,cx,ax - are all zeroed out before entry.		 	   *
;*                                                                         *
;* OUTPUT:                                                                 *
;*                                                                         *
;* WARNINGS:                                                               *
;*                                                                         *
;* HISTORY:                                                                *
;*   03/09/1992  SB : Created.                                             *
;*=========================================================================*
Copy_Delta_Buffer proc C nextrow:DWORD

	;ARG	nextrow:DWORD

top_loop3:
	xor	eax,eax			; clear out eax.
	mov	al,[esi]		; get delta source byte
	inc	esi

	test	al,al			; check for a SHORTDUMP ; check al incase of sign value.
	je	short_run3
	js	check_others3

;
; SHORTDUMP
;
	mov	ecx,eax			; stick count in cx

dump_loop3:
	mov	al,[esi]		; get delta XOR byte

	mov	[edi],al		; store that byte on the dest

	inc	edx			; increment our count on current column
	inc	esi
	inc	edi
	cmp	edx,ebx			; are we at the final column
	jne	end_col1_3		; if not the jmp over the code

	sub	edi,edx			; get our column back to the beginning.
	xor	edx,edx			; zero out our column counter
	add	edi,[nextrow]		; jump to start of next row
end_col1_3:

	dec	ecx
	jnz	dump_loop3
	jmp	top_loop3

;
; SHORTRUN
;

short_run3:
	mov	cl,[esi]		; get count
	inc	esi			; inc delta source

do_run3:
	mov	al,[esi]		; get XOR byte
	inc	esi

run_loop3:
	mov	[edi],al		; store the byte (instead of XOR against current color)

	inc	edx			; increment our count on current column
	inc	edi			; go to next dest pixel
	cmp	edx,ebx			; are we at the final column
	jne	end_col2_3		; if not the jmp over the code

	sub	edi,ebx			; get our column back to the beginning.
	xor	edx,edx			; zero out our column counter
	add	edi,[nextrow]		; jump to start of next row
end_col2_3:


	dec	ecx
	jnz	run_loop3
	jmp	top_loop3

;
; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP
;

check_others3:
	sub	eax,080h		; opcode -= 0x80
	jnz	do_skip3		; if zero then get next word, otherwise use remainder.

	mov	ax,[esi]		; get word code in ax
	lea	esi,[esi+2]
	test	ax,ax			; set flags. (not 32bit register so neg flag works)
	jle	not_long_skip3

;
; SHORTSKIP AND LONGSKIP
;
do_skip3:
	sub	edi,edx			; go back to beginning or row.
	add	edx,eax			; incriment our count on current row
recheck3:
	cmp	edx,ebx			; are we past the end of the row
	jb	end_col3_3  		; if not the jmp over the code

	sub	edx,ebx			; Subtract width from the col counter
	add	edi,[nextrow]  		; jump to start of next row
	jmp	recheck3		; jump up to see if we are at the right row
end_col3_3:
	add	edi,edx			; get to correct position in row.
	jmp	top_loop3


not_long_skip3:
	jz	stop3			; long count of zero means stop
	sub	eax,08000h     		; opcode -= 0x8000
	test	eax,04000h		; is it a LONGRUN (code & 0x4000)?
	je	long_dump3

;
; LONGRUN
;
	sub	eax,04000h		; opcode -= 0x4000
	mov	ecx,eax			; use cx as loop count
	jmp	do_run3		; jump to run code.


;
; LONGDUMP
;

long_dump3:
	mov	ecx,eax			; use cx as loop count
	jmp	dump_loop3		; go to the dump loop.

stop3:

	ret

Copy_Delta_Buffer	ENDP	

;----------------------------------------------------------------------------

	END


;----------------------------------------------------------------------------
;
;PUBLIC UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr)
;{
;
;	register UWORD loop;
;	BYTE opcode, xor_byte;
;	UWORD bytes_to_uncompress = 64000U;
;
;
;	/* Make our buffer pointer */
;
;	to    = MK_FP(page_seg, 0);
;	delta = Normalize_Pointer(delta_ptr);
;
;
;	while (bytes_to_uncompress) {
;
;		opcode = *delta++;
;
;
;		/* Check for SHORTDUMP */
;
;		if (opcode > 0) {
;
;
;			bytes_to_uncompress -= opcode;
;
;			for (loop = 0; loop < opcode; loop++) {
;				xor_byte = *delta++;
;				*to++ ^= xor_byte;
;			}
;			continue;
;		}
;
;		/* Check for SHORTRUN */
;
;		if (opcode == 0) {
;
;			word_count = *delta++;
;			xor_byte   = *delta++;
;
;			bytes_to_uncompress -= word_count;
;
;			for (loop = 0; loop < word_count; loop++) {
;				*to++ ^= xor_byte;
;			}
;			continue;
;		}
;
;		/* By now, we know it must be a LONGDUMP, SHORTSKIP, or LONGSKIP */
;
;		opcode -= 0x80;
;
;
;		/* Is it a SHORTSKIP? */
;
;		if (opcode != 0) {
;
;			to += opcode;
;			bytes_to_uncompress -= (WORD) opcode;
;			continue;
;		}
;
;
;		word_count = *((UWORD *) delta)++;
;
;		/* Is it a LONGSKIP? */
;
;		if ((WORD) word_count > 0) {
;
;			to += word_count;
;			bytes_to_uncompress -= (WORD) word_count;
;			continue;
;		}
;
;
;		word_count -= 0x8000;
;
;		/* Is it a LONGRUN? */
;
;		if (word_count & 0x4000) {
;
;			word_count -= 0x4000;
;
;			bytes_to_uncompress -= word_count;
;
;			xor_byte = *delta++;
;
;			for (loop = 0; loop < word_count; loop++) {
;				*to++ ^= xor_byte;
;			}
;			continue;
;		}
;
;
;		/* It must be a LONGDUMP */
;
;		bytes_to_uncompress -= word_count;
;
;		for (loop = 0; loop < word_count; loop++) {
;			xor_byte = *delta++;
;			*to++ ^= xor_byte;
;		}
;	}
;
;
;	return(64000U);
;}
;

