CnC_Red_Alert/WIN32LIB/AUDIO/OLSOSDEC.ASM

756 lines
19 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/>.
;
;****************************************************************************
;*
;* Copyright (c) 1994, HMI, INC. All Rights Reserved
;*
;*---------------------------------------------------------------------------
;*
;* FILE
;* soscodec.asm
;*
;* DESCRIPTION
;* HMI SOS ADPCM compression/decompression.
;*
;* PROGRAMMER
;* Nick Skrepetos
;* Denzil E. Long, Jr. (Fixed bugs, rewrote for watcom)
;* Bill Petro (Added stereo support)
;* DATE
;* Febuary 15, 1995
;*
;*---------------------------------------------------------------------------
;*
;* PUBLIC
;*
;****************************************************************************
IDEAL
P386
MODEL USE32 FLAT
LOCALS ??
DPMI_INTR equ 31h
IF_LOCKED_PM_CODE equ 1h ; Locked PM code for DPMI.
IF_LOCKED_PM_DATA equ 2h ; Locked PM code for DPMI.
STRUC sCompInfo
lpSource DD ? ;Compressed data pointer
lpDest DD ? ;Uncompressed data pointer
dwCompSize DD ? ;Compressed size
dwUnCompSize DD ? ;Uncompressed size
dwSampleIndex DD ? ;Index into sample
dwPredicted DD ? ;Next predicted value
dwDifference DD ? ;Difference from last sample
wCodeBuf DW ? ;Holds 2 nibbles for decompression
wCode DW ? ;Current 4 bit code
wStep DW ? ;Step value in table
wIndex DW ? ;Index into step table
dwSampleIndex2 DD ? ;Index into sample
dwPredicted2 DD ? ;Next predicted value
dwDifference2 DD ? ;Difference from last sample
wCodeBuf2 DW ? ;Holds 2 nibbles for decompression
wCode2 DW ? ;Current 4 bit code
wStep2 DW ? ;Step value in table
wIndex2 DW ? ;Index into step table
wBitSize DW ? ;Bit size for decompression
wChannels DW ? ;number of channels
ENDS sCompInfo
DATASEG
InitFlags DD 0 ; Flags to indicate what has been initialized.
LABEL LockedDataStart BYTE
;* Index table for stepping into step table
wCODECIndexTab DW -1,-1,-1,-1,2,4,6,8
DW -1,-1,-1,-1,2,4,6,8
;Lookup table of replacement values
;The actual sound value is replaced with an index to lookup in this table
;The index only takes up a nibble(4bits) and represents an int(16bits)
;Essentially:
;Get a value
;compare it with the value before it
;find closest value in table and store the index into the table
;if i'm going down then negitize it
;go to next byte.
;Theory for stereo:
;1)handle stereo and mono in two seperate loops. cleaner...
;start at byte 0 and skip every other byte(or word) both write and read
;when we get done
; set start byte to 1 and do it again
;This table essentialy round off to closes values in 3 distinct bands
; precalculated and optimized(i guess) for human hearing.
wCODECStepTab DW 7, 8, 9, 10, 11, 12, 13,14
DW 16, 17, 19, 21, 23, 25, 28, 31
DW 34, 37, 41, 45, 50, 55, 60, 66
DW 73, 80, 88, 97, 107, 118, 130, 143
DW 157, 173, 190, 209, 230, 253, 279, 307
DW 337, 371, 408, 449, 494, 544, 598, 658
DW 724, 796, 876, 963, 1060, 1166, 1282, 1411
DW 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024
DW 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484
DW 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899
DW 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794
DW 32767
;dwCODECByteIndex DD 0 ; this is when to stop compressing
;dwCODECTempStep DD 0 ; tempory storage for step value
;wCODECMask DW 0 ; Current mask
LABEL LockedDataEnd BYTE
CODESEG
LABEL LockedCodeStart BYTE
;****************************************************************************
;*
;* NAME
;* sosCODECInitStream - Initialize compression stream.
;*
;* SYNOPSIS
;* sosCODECInitStream(CompInfo)
;*
;* void sosCODECInitStream(_SOS_COMPRESS_INFO *);
;*
;* FUNCTION
;* Initialize compression stream for compression and decompression.
;*
;* INPUTS
;* CompInfo - Compression information structure.
;*
;* RESULT
;* NONE
;*
;****************************************************************************
GLOBAL C sosCODECInitStream:NEAR
PROC sosCODECInitStream C NEAR
ARG sSOSInfo:NEAR PTR
mov eax,[sSOSInfo]
mov [(sCompInfo eax).wIndex],0 ; starting index 0
mov [(sCompInfo eax).wStep],7 ; start with a step of 7
mov [(sCompInfo eax).dwPredicted],0 ; no predicted value
mov [(sCompInfo eax).dwSampleIndex],0 ;start at head of index
mov [(sCompInfo eax).wIndex2],0 ; starting index 0
mov [(sCompInfo eax).wStep2],7 ; start with a step of 7
mov [(sCompInfo eax).dwPredicted2],0 ; no predicted value
mov [(sCompInfo eax).dwSampleIndex2],0 ;start at head of index
ret
ENDP sosCODECInitStream
;****************************************************************************
;*
;* NAME
;* sosCODECDecompressData - Decompress audio data.
;*
;* SYNOPSIS
;* Size = sosCODECDecompressData(CompInfo, NumBytes)
;*
;* long sosCODECDecompressData(_SOS_COMPRESS_INFO *, long);
;*
;* FUNCTION
;* Decompress data from a 4:1 ADPCM compressed stream. The number of
;* bytes decompressed is returned.
;*
;* INPUTS
;* CompInfo - Compress information structure.
;* NumBytes - Number of bytes to compress.
;*
;* RESULT
;* Size - Size of decompressed data.
;*
;****************************************************************************
GLOBAL C General_sosCODECDecompressData:NEAR
PROC General_sosCODECDecompressData C NEAR
ARG sSOSInfo:NEAR PTR
ARG wBytes:DWORD
local dwCODECBytesProcessed:dword ;bytes to decompress
local dwCODECByteIndex:dword ;this is when to stop compressing
; these need to be local if the function is to be reenterant
push esi
push edi
push ebx
push ecx
push edx
;*---------------------------------------------------------------------------
;* Initialize
;*---------------------------------------------------------------------------
mov ebx,[sSOSInfo]
mov eax,[wBytes]
mov [dwCODECBytesProcessed],eax
mov [(sCompInfo ebx).dwSampleIndex],0 ;start at head of index
mov [(sCompInfo ebx).dwSampleIndex2],0 ;start at head of index
;Check for 16 bit decompression
cmp [(sCompInfo ebx).wBitSize],16
jne short ??skipByteDivide
shr eax,1 ;Divide size by two
??skipByteDivide:
mov [dwCODECByteIndex],eax
mov esi,[(sCompInfo ebx).lpSource]
mov edi,[(sCompInfo ebx).lpDest]
cmp [(sCompInfo ebx).wChannels],2 ;stereo check
je ??mainloopl ;do left side first
; Determine if sample index is even or odd. This will determine
; if we need to get a new token or not.
;---------------------------------------------------------------------------
;Main Mono Loop
;---------------------------------------------------------------------------
??mainloop:
test [(sCompInfo ebx).dwSampleIndex],1 ;odd ??
je short ??fetchToken ; if so get new token
xor eax,eax ;else shift int codebuf
mov ax,[(sCompInfo ebx).wCodeBuf] ;ored with Code
shr eax,4
and eax,000Fh
mov [(sCompInfo ebx).wCode],ax
jmp short ??calcDifference
??fetchToken:
xor eax,eax ;get a new token
mov al,[esi] ;put in codebuf
mov [(sCompInfo ebx).wCodeBuf],ax
inc esi
and eax,000Fh
mov [(sCompInfo ebx).wCode],ax ;and then code
??calcDifference:
mov [(sCompInfo ebx).dwDifference],0 ;reset diff
xor ecx,ecx
mov cx,[(sCompInfo ebx).wStep] ;cx is step value
test eax,4 ;Check for wCode & 4
je short ??no4
add [(sCompInfo ebx).dwDifference],ecx ;Add wStep
??no4:
test eax,2 ;Check for wCode & 2
je short ??no2
mov edx,ecx ;Add wStep >> 1
shr edx,1
add [(sCompInfo ebx).dwDifference],edx
??no2:
test eax,1 ;Check for wCode & 1
je short ??no1
mov edx,ecx ;Add wStep >> 2
shr edx,2
add [(sCompInfo ebx).dwDifference],edx
??no1:
mov edx,ecx ;Add in wStep >> 3
shr edx,3
add [(sCompInfo ebx).dwDifference],edx
test eax,8 ;Check for wCode & 8
je short ??no8
neg [(sCompInfo ebx).dwDifference] ;Negate diff
??no8:
; add difference to predicted value.
mov eax,[(sCompInfo ebx).dwPredicted]
add eax,[(sCompInfo ebx).dwDifference]
; make sure there is no under or overflow.
cmp eax,7FFFh
jl short ??noOverflow
mov eax,7FFFh
??noOverflow:
cmp eax,0FFFF8000h
jg short ??noUnderflow
mov eax,0FFFF8000h
??noUnderflow:
mov [(sCompInfo ebx).dwPredicted],eax
cmp [(sCompInfo ebx).wBitSize],16
jne short ??output8Bit
mov [edi],ax ;Output 16bit sample
add edi,2
jmp short ??adjustIndex
??output8Bit:
; output 8 bit sample
xor ah,80h
mov [edi],ah
inc edi
??adjustIndex:
xor ecx,ecx
mov cx,[(sCompInfo ebx).wCode]
xor eax,eax
shl ecx,1
mov ax,[wCODECIndexTab + ecx]
add [(sCompInfo ebx).wIndex],ax ;check if wIndex < 0
cmp [(sCompInfo ebx).wIndex],8000h
jb short ??checkOverflow
mov [(sCompInfo ebx).wIndex],0 ;reset index to zero
jmp short ??adjustStep
??checkOverflow:
cmp [(sCompInfo ebx).wIndex],88 ;check if wIndex > 88
jbe short ??adjustStep
mov [(sCompInfo ebx).wIndex],88 ;reset index to 88
??adjustStep:
; fetch wIndex so we can fetch new step value
xor ecx,ecx
mov cx,[(sCompInfo ebx).wIndex]
xor eax,eax
shl ecx,1
mov ax,[wCODECStepTab + ecx]
; advance index and store step value
add [(sCompInfo ebx).dwSampleIndex],1
mov [(sCompInfo ebx).wStep],ax
; decrement bytes processed and loop back.
dec [dwCODECByteIndex]
jne ??mainloop
jmp ??exitout
;--------------------------------------------------------------------------
;Left Channel Start
;--------------------------------------------------------------------------
??mainloopl:
test [(sCompInfo ebx).dwSampleIndex],1
je short ??fetchTokenl
xor eax,eax
mov ax,[(sCompInfo ebx).wCodeBuf]
shr eax,4
and eax,000Fh
mov [(sCompInfo ebx).wCode],ax
jmp short ??calcDifferencel
??fetchTokenl:
xor eax,eax
mov al,[esi]
mov [(sCompInfo ebx).wCodeBuf],ax
add esi,2 ;2 for stereo
and eax,000Fh
mov [(sCompInfo ebx).wCode],ax
??calcDifferencel:
; reset difference
mov [(sCompInfo ebx).dwDifference],0
xor ecx,ecx
mov cx,[(sCompInfo ebx).wStep]
test eax,4 ;Check for wCode & 4
je short ??no4l
add [(sCompInfo ebx).dwDifference],ecx ;Add wStep
??no4l:
test eax,2 ;Check for wCode & 2
je short ??no2l
mov edx,ecx ;Add wStep >> 1
shr edx,1
add [(sCompInfo ebx).dwDifference],edx
??no2l:
test eax,1 ;Check for wCode & 1
je short ??no1l
mov edx,ecx ;Add wStep >> 2
shr edx,2
add [(sCompInfo ebx).dwDifference],edx
??no1l:
mov edx,ecx ;Add in wStep >> 3
shr edx,3
add [(sCompInfo ebx).dwDifference],edx
test eax,8 ;Check for wCode & 8
je short ??no8l
neg [(sCompInfo ebx).dwDifference] ;Negate diff
??no8l:
; add difference to predicted value.
mov eax,[(sCompInfo ebx).dwPredicted]
add eax,[(sCompInfo ebx).dwDifference]
; make sure there is no under or overflow.
cmp eax,7FFFh
jl short ??noOverflowl
mov eax,7FFFh
??noOverflowl:
cmp eax,0FFFF8000h
jg short ??noUnderflowl
mov eax,0FFFF8000h
??noUnderflowl:
mov [(sCompInfo ebx).dwPredicted],eax
cmp [(sCompInfo ebx).wBitSize],16
jne short ??output8Bitl
mov [edi],ax ;Output 16bit sample
add edi,4 ;4 for stereo
jmp short ??adjustIndexl
??output8Bitl:
; output 8 bit sample
xor ah,80h
mov [edi],ah
add edi,2 ;2 for stereo
??adjustIndexl:
xor ecx,ecx
mov cx,[(sCompInfo ebx).wCode]
xor eax,eax
shl ecx,1
mov ax,[wCODECIndexTab + ecx]
add [(sCompInfo ebx).wIndex],ax
; check if wIndex < 0
cmp [(sCompInfo ebx).wIndex],8000h
jb short ??checkOverflowl
mov [(sCompInfo ebx).wIndex],0
jmp short ??adjustStepl ;reset index to zero
??checkOverflowl:
cmp [(sCompInfo ebx).wIndex],88 ; check if wIndex > 88
jbe short ??adjustStepl
mov [(sCompInfo ebx).wIndex],88 ; reset index to 88
??adjustStepl:
; fetch wIndex so we can fetch new step value
xor ecx,ecx
mov cx,[(sCompInfo ebx).wIndex]
xor eax,eax
shl ecx,1
mov ax,[wCODECStepTab + ecx]
; advance index and store step value
add [(sCompInfo ebx).dwSampleIndex],1
mov [(sCompInfo ebx).wStep],ax
; decrement bytes processed and loop back.
sub [dwCODECByteIndex],2
jne ??mainloopl
;----------------------------------------------------------------------------
; Right Side Setup
;----------------------------------------------------------------------------
mov eax,[wBytes]
mov [dwCODECBytesProcessed],eax
mov esi,[(sCompInfo ebx).lpSource]
mov edi,[(sCompInfo ebx).lpDest]
inc esi ; skip left channel
inc edi ; skip left channel
cmp [(sCompInfo ebx).wBitSize],16 ;16 bit ??
je short ??doByteDivide
mov [dwCODECByteIndex],eax
jmp short ??mainloopr
??doByteDivide:
shr eax,1 ;Divide size by two
inc edi ; 16 bit so skip 1 more
mov [dwCODECByteIndex],eax
;--------------------------------------------------------------------------
;Right Channel Start
;--------------------------------------------------------------------------
??mainloopr:
test [(sCompInfo ebx).dwSampleIndex2],1
je short ??fetchTokenr
xor eax,eax
mov ax,[(sCompInfo ebx).wCodeBuf2]
shr eax,4
and eax,000Fh
mov [(sCompInfo ebx).wCode2],ax
jmp short ??calcDifferencer
??fetchTokenr:
xor eax,eax
mov al,[esi]
mov [(sCompInfo ebx).wCodeBuf2],ax
add esi,2 ;2 for stereo
and eax,000Fh
mov [(sCompInfo ebx).wCode2],ax
??calcDifferencer:
; reset difference
mov [(sCompInfo ebx).dwDifference2],0
xor ecx,ecx
mov cx,[(sCompInfo ebx).wStep2]
test eax,4 ;Check for wCode & 4
je short ??no4r
add [(sCompInfo ebx).dwDifference2],ecx ;Add wStep
??no4r:
test eax,2 ;Check for wCode & 2
je short ??no2r
mov edx,ecx ;Add wStep >> 1
shr edx,1
add [(sCompInfo ebx).dwDifference2],edx
??no2r:
test eax,1 ;Check for wCode & 1
je short ??no1r
mov edx,ecx ;Add wStep >> 2
shr edx,2
add [(sCompInfo ebx).dwDifference2],edx
??no1r:
mov edx,ecx ;Add in wStep >> 3
shr edx,3
add [(sCompInfo ebx).dwDifference2],edx
test eax,8 ;Check for wCode & 8
je short ??no8r
neg [(sCompInfo ebx).dwDifference2] ;Negate diff
??no8r:
; add difference to predicted value.
mov eax,[(sCompInfo ebx).dwPredicted2]
add eax,[(sCompInfo ebx).dwDifference2]
cmp eax,7FFFh
jl short ??noOverflowr
mov eax,7FFFh
??noOverflowr:
cmp eax,0FFFF8000h
jg short ??noUnderflowr
mov eax,0FFFF8000h
??noUnderflowr:
mov [(sCompInfo ebx).dwPredicted2],eax
cmp [(sCompInfo ebx).wBitSize],16
jne short ??output8Bitr
mov [edi],ax ;Output 16bit sample
add edi,4 ;4 for stereo ***
jmp short ??adjustIndexr
??output8Bitr:
; output 8 bit sample
xor ah,80h
mov [edi],ah
add edi,2 ;2 for stereo
??adjustIndexr:
xor ecx,ecx
mov cx,[(sCompInfo ebx).wCode2]
xor eax,eax
shl ecx,1
mov ax,[wCODECIndexTab + ecx]
add [(sCompInfo ebx).wIndex2],ax
; check if wIndex < 0
cmp [(sCompInfo ebx).wIndex2],8000h
jb short ??checkOverflowr
; reset index to zero
mov [(sCompInfo ebx).wIndex2],0
jmp short ??adjustStepr
??checkOverflowr:
; check if wIndex > 88
cmp [(sCompInfo ebx).wIndex2],88
jbe short ??adjustStepr
mov [(sCompInfo ebx).wIndex2],88 ; reset index to 88
??adjustStepr:
; fetch wIndex so we can fetch new step value
xor ecx,ecx
mov cx,[(sCompInfo ebx).wIndex2]
xor eax,eax
shl ecx,1
mov ax,[wCODECStepTab + ecx]
; advance index and store step value
add [(sCompInfo ebx).dwSampleIndex2],1
mov [(sCompInfo ebx).wStep2],ax
; decrement bytes processed and loop back.
sub [dwCODECByteIndex],2
jne ??mainloopr
??exitout:
; don't think we need this but just in case i'll leave it here!!
; mov [(sCompInfo ebx).lpSource],esi
; mov [(sCompInfo ebx).lpDest],edi
; set up return value for number of bytes processed.
mov eax,[dwCODECBytesProcessed]
pop edx
pop ecx
pop ebx
pop edi
pop esi
ret
ENDP General_sosCODECDecompressData
LABEL LockedCodeEnd BYTE
;***************************************************************************
;* sosCODEC_LOCK -- locks the JLB audio decompression code *
;* *
;* INPUT: none *
;* *
;* OUTPUT: BOOL true is lock sucessful, false otherwise *
;* *
;* PROTO: BOOL sosCODEC_Lock(void); *
;* *
;* HISTORY: *
;* 06/26/1995 PWG : Created. *
;*=========================================================================*
GLOBAL C sosCODEC_Lock:NEAR
PROC sosCODEC_Lock C NEAR USES ebx ecx edx esi edi
;
; Lock the code that is used by the sos decompression method.
;
mov eax,0600h ; function number.
mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
mov edi,OFFSET LockedCodeEnd ; edi will have size of region in bytes.
shld ebx,ecx,16
sub edi, ecx
shld esi,edi,16
int DPMI_INTR ; do call.
jc ??error
or [InitFlags], IF_LOCKED_PM_CODE
;
; Lock the data used by the sos decompression method.
;
mov eax,0600h ; function number.
mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
mov edi,OFFSET LockedDataEnd ; edi will have size of region in bytes.
shld ebx,ecx,16
sub edi, ecx
shld esi,edi,16
int DPMI_INTR ; do call.
jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
or [InitFlags], IF_LOCKED_PM_DATA
mov eax,1
jmp ??exit
??error:
xor eax,eax
??exit:
ret
ENDP sosCODEC_Lock
;***************************************************************************
;* DECOMPRESS_FRAME_UNLOCK -- Unlocks the JLB audio compression code *
;* *
;* INPUT: none *
;* *
;* OUTPUT: BOOL true is unlock sucessful, false otherwise *
;* *
;* PROTO: BOOL sosCODEC_Unlock(void); *
;* *
;* HISTORY: *
;* 06/26/1995 PWG : Created. *
;*=========================================================================*
GLOBAL C sosCODEC_Unlock:NEAR
PROC sosCODEC_Unlock C NEAR USES ebx ecx edx esi edi
test [InitFlags],IF_LOCKED_PM_CODE
jz ??code_not_locked
mov eax , 0601h
mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
mov edi,OFFSET LockedCodeEnd ; edx will have size of region in bytes.
sub edi,ecx ; - figure size.
shld ebx , ecx , 16
shld esi , edi , 16
int DPMI_INTR ; do call.
jc ??error
??code_not_locked:
test [InitFlags],IF_LOCKED_PM_DATA
jz ??data_not_locked
mov ax,0601h ; set es to descriptor of data.
mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
mov edi,OFFSET LockedDataEnd ; edx will have size of region in bytes.
sub edi,ecx ; - figure size.
shld ebx , ecx , 16
shld esi , edi , 16
int DPMI_INTR ; do call.
jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
??data_not_locked:
mov [InitFlags],0
mov eax,1
jmp ??exit
??error:
xor eax,eax
??exit:
ret
ENDP sosCODEC_Unlock
END